Archive : 2008-07

追記:やっぱり問題が発生するようです。エントリ内の写真が消えたりする問題が起きるようなので、この方法も駄目でした…orz

ひとつ前のエントリ(中まで知ろう!Windows Live Writer)で、Windows Live Writerの、SDKには載っていないクラスを色々と紹介しました。その内容をうけて、このエントリではPublish Notification Hook プラグインで、投稿前に記事内容を書き換える方法を紹介したいと思います。以前にも、SharpLab. – publish notification hooksでpost内容を書き換えるにはというエントリでpublish notification hooksプラグインで投稿内容を書き換える方法を紹介したことがありました。ですが、

WLW 自体は書き換えられることを想定してないので、プラグインから書き換えても投稿後にエディタ中の表示は変化しません。

Publish Notification Hook プラグイン 値の書き換え-katamari.wankuma.com

というJZ5さんの検証エントリの通り、投稿後の画面がおかしくなる問題がありました。今回の方法ではその回避を図っています。

とりあえずソースコードを示します。

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using WindowsLive.Writer.Api;
using WindowsLive.Writer.BlogClient;
using WindowsLive.Writer.BlogClient.Clients;
using WindowsLive.Writer.PostEditor;
using WindowsLive.Writer.Extensibility.BlogClient;

using WriterMshtml = WindowsLive.Writer.Mshtml;

namespace SharpLab.WPCustomFieldsEditor {
    [WriterPlugin("0aaad6ce-da8b-4f78-a73c-c73f3c6c163d", "WPCustomFieldsEditor", PublisherUrl = "http://www.sharplab.net/")]
    public class PublishObserver : PublishNotificationHook {
        public override bool OnPrePublish(System.Windows.Forms.IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish) {

            PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);
            Blog blog = new Blog(postEditorForm.CurrentBlogId);

            //publishingContextからリフレクションでBlogPostEditingManagerを取得
            FieldInfo fieldInfo = publishingContext.GetType().GetField("_editingContext", BindingFlags.NonPublic | BindingFlags.Instance);
            BlogPostEditingManager manager = (BlogPostEditingManager)fieldInfo.GetValue(publishingContext);
            BlogPost blogPost = (BlogPost)publishingContext.PostInfo;

            blogPost.Title = "test";//エントリ内容への操作

            manager.EditPost(new BlogPostEditingContext(postEditorForm.CurrentBlogId, blogPost));

            return true;
        }

        private PostEditorForm getPostEditorForm(IWin32Window postProgressForm) {
            return (PostEditorForm)((Form)Form.FromHandle(postProgressForm.Handle)).Owner;
        }
    }
}

細かく見ていきましょう。

            PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);

この行ではPostEditorFormクラスのインスタンスを取得しています。前エントリ参照。

           Blog blog = new Blog(postEditorForm.CurrentBlogId);

この行ではBlogクラスのインスタンスを取得しています。前エントリ参照。

            FieldInfo fieldInfo = publishingContext.GetType().GetField("_editingContext", BindingFlags.NonPublic | BindingFlags.Instance);
            BlogPostEditingManager manager = (BlogPostEditingManager)fieldInfo.GetValue(publishingContext);

imageここでは、BlogPostEditingManagerクラスのインスタンスを取得してます。OnPrePublishメソッドの引数として渡されたpublishingContextにはBlogPostHtmlEditorクラス(WindowsLive.Writer.PostEditor.PostHtmlEditing名前空間に所属)のインスタンスが格納されており、その_editingContextというパブリックでないフィールドにBlogPostEditingManagerが格納されています。BlogPostHtmlEditorクラス自体もパブリックなクラスではないので、ここではリフレクションで取得しています。なお、BlogPostEditingManagerクラスはエントリを編集画面に読み込んだり、削除したりできるクラスです。正直、自分も良く分かっていません(笑)。

 

            BlogPost blogPost = (BlogPost)publishingContext.PostInfo;

ここでは編集対象のエントリを表すBlogPostクラスのインスタンスを取得しています。前エントリ参照。

            blogPost.Title = "test";//エントリ内容への操作

エントリ内容への書き換えをここで行うことが出来ます。この例ではタイトルの書き換えをしています。試したわけではありませんが、カテゴリや概要に対して変更を加えることもできる筈です。

            manager.EditPost(new BlogPostEditingContext(postEditorForm.CurrentBlogId, blogPost));

そして、書き換えた後の大事な後始末、書き換えたエントリのPostEditorEormへの再読み込みをここで行っております。BlogPostEditingManagerクラスのEditPostメソッドに、それっぽい引数を与えて呼び出せば読み込んでくれるようです。

JUL
30

WYSIWYG編集が可能で、Wordを扱うかのような感覚でBlogをサクサク書くことが出来るMS謹製投稿クライアントWindows Live Writer。本体だけでも相当強力なソフトですが、機能強化のための各種プラグインを開発することもできるようになっており、.NETをかじっている自分にはなかなか興味をそそられるソフトでもあります。その開発の仕方はJZ5さんこと、松江祐輔さんが書かれた使ってみよう! Windows Live SDK/API:第7回 Windows Live Writer … 技術評論社という記事がチュートリアルとしてとても分かり易いものとなっています。詳しいリファレンスとしてはWindows Live Writer SDKが現在の正式版に対応する公式で、Writer DevZone: Technical Preview: Now Available for Downloadにこの前出たTecnology Preview版のSDKが転がっています。どっちも英語ですけど。

で、このエントリでは、ここ数日オブジェクトブラウザを通じてWindows Live WriterのDLL群とにらめっこした結果わかった、SDKに載っていない本体の内部構造の話をしてみようかと。Windows Live Writerの中にどんなクラスがあるかや、どうやってそのインスタンスをプラグイン側から取得するかのお話。

Windows Live WriterのメインFormの取得方法

image 何はなくとも、記事編集画面が無ければ始まらない。Windows Live Writerの記事編集画面のFormのインスタンスをプラグインから取得する方法から説明を始めます。プラグインが呼び出された時に呼ばれるメソッドのうちいくつかは、dialogOwnerという、System.Windows.Forms.IWin32Window型の引数を持っています。このdialogOwnerという引数が今回のカギとなります。読んでの通り、dialogOwnerは親Formを示しており、SmartContentSource.CreateContentメソッドなら、記事編集画面のForm、PublishNotificationHook.OnPrePublishメソッドやOnPostPublishメソッドなら投稿中のプログレス表示を行うFormとなります。

        private PostEditorForm getPostEditorForm(IWin32Window postProgressForm) {
            return (PostEditorForm)((Form)Form.FromHandle(postProgressForm.Handle)).Owner;
        }

PublishNotificationHookの方から取得できるdialogOwnerからメインFormのインスタンスを取得したいならば、上記のようなメソッドを用意して取得すればOK。見ての通り、メインFormのクラス名はPostEditorFormで、WindowsLive.Writer.PostEditor名前空間に所属しているクラスです。今後は記事編集画面のFormのことはPostEditorFormと呼ぶことにします。

Blogクラスのインスタンスの取得

imageさて、前の項で取得できたPostEditorFormクラスには、CurrentBlogIdというプロパティがあります。このプロパティには、Windows Live Writerで管理しているBlogを識別するためのGUIDが入っており、これを基に現在編集対象としているBlogを表すBlogクラスのインスタンスを取得可能です。こんな感じに。↓

            Blog blog = new Blog(postEditorForm.CurrentBlogId);

BlogクラスはWindowsLive.Writer.BlogClient名前空間に所属しているクラスで、メンバには記事の取得や投稿に関するメソッドなど、Blogに関する様々な操作を実現するメソッドやプロパティが含まれています。Blogクラスをオブジェクトブラウザで表示したもののSSを示しましたが、サイズの関係でSSに写っていないものもたくさんあるので、是非自分で確かめてみてください。

BlogClientクラスのインスタンスの取得

ところでBlogクラスには、パブリックではないプロパティとしてBlogClientというプロパティがあります。このプロパティには、IBlogClient(WindowsLive.Writer.Extensibility.BlogClient名前空間に所属)というインターフェイスを実装したクラスのインスタンスが含まれています。一例を挙げると、WordPressClientクラスや、MovableTypeClientクラス、WindowsLiveSpacesClientクラス(全てWindowsLive.Writer.BlogClient.Clients名前空間に所属)などです。これらにはUsernameや、Passwordというプロパティが含まれており、自前でXML-RPCを使ってサーバーとやり取りしたい場合などに活用できます。(WordPressはMetaWeblobAPIの拡張に熱心であるので、Windows Live Writerが対応していない機能もあり、意外と使いたくなる場面があるものです)

publishingContext.PostInfoの実体

PublishNotificationHook.OnPrePublishメソッドやOnPostPublishメソッドのpublishingContextという引数には、PostInfoというIPostInfo型のプロパティがあるのは御存知だと思います。以前自分はこのプロパティに格納されているインスタンスの、IPostInfoインターフェイスでは読み取り専用になっているプロパティに対してリフレクションで値をセットするコードがなぜ動くのかが分からないとかぼやいていました

調べてみたところ、リフレクションでアクセスするのは、参照するのに使われたインターフェイスのメンバではなく、インスタンスのメンバだから、ということのようです。用語法をあまり理解できていないので、おかしな説明になっているかもしれません。要するに、PostInfoに対してリフレクションでアクセスできるのは、IPostInfoインターフェイスのメンバだけではなく、BlogPostクラスのメンバなのだ、ということです。これなら前のエントリのpi.CanWriteの部分がtrueを返すのも納得できます。

まぁとりあえずpublishingContext.PostInfoにはBlogPostクラスのインスタンスが入っているということです。そこで、こんな感じにキャストしてやると、

            BlogPost blogPost = (BlogPost)publishingContext.PostInfo;

よりエントリの情報を詳細に得ることができるようになります。投稿スラッグや概要(Excerpt)、投稿日時を表すっぽいプロパティもあるので、チェックしてみてはいかがでしょうか。

次のエントリでは、これらを利用して、Publish Notification Hook プラグインで、投稿前に記事内容を書き換える方法を紹介したいと思います。プラグインからの書き換え後、エディタ中の表示が変化しない問題を解決するために、エントリを管理しているクラスの説明も少しだけします。

前のエントリでは

表示モードを「HTMLコード」としていた場合、HTMLDocumentに対してプラグインから操作後に表示モードを「標準」に戻すと、HTMLDocumentに対する操作内容が飛んでしまう

という問題で躓いていたが、HTMLDocumentに対して操作を行う前に表示モードを「標準」にしておくことで対処できた。

こんな感じ。HTMLDocumentを直接操作できるようになって、うはー 夢が広がりんぐ

なお、表示モードの切り替えがタブ式になったTechnology Preview版限定。

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using WindowsLive.Writer.Api;
using WindowsLive.Writer.BlogClient;
using WindowsLive.Writer.BlogClient.Clients;
using WindowsLive.Writer.PostEditor;
using WindowsLive.Writer.Extensibility.BlogClient;

using WriterMshtml = WindowsLive.Writer.Mshtml;

namespace SharpLab.WPCustomFieldsEditor {
    [WriterPlugin("0aaad6ce-da8b-4f78-a73c-c73f3c6c163d", "WPCustomFieldsEditor", PublisherUrl = "http://www.sharplab.net/")]
    public class PublishObserver : PublishNotificationHook {
        public override bool OnPrePublish(System.Windows.Forms.IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish) {

            PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);

            WindowsLive.Writer.Controls.MiniTabsControl MiniTabsControl = (WindowsLive.Writer.Controls.MiniTabsControl)findControlByName(postEditorForm, "MiniTabsControl", "WindowsLive.Writer.Controls");
            MiniTabsControl.SelectTab(0);

            WriterMshtml.MshtmlEditor mshtmlEditor = (WriterMshtml.MshtmlEditor)findControlByName(postEditorForm, "MshtmlEditor", "WindowsLive.Writer.Mshtml");
            mshtml.IHTMLDocument2 doc = mshtmlEditor.HTMLDocument;
            //あとは煮るなり焼くなり。
        }

        private PostEditorForm getPostEditorForm(IWin32Window postProgressForm) {
            return (PostEditorForm)((Form)Form.FromHandle(postProgressForm.Handle)).Owner;
        }

        private Control findControlByName(Control control, string controlName, string controlNamespace) {

            foreach (Control cChild in control.Controls) {

                Type t = cChild.GetType();

                if (t.Name == controlName && t.Namespace == controlNamespace) {
                    return cChild;
                }

                if (cChild.HasChildren) {
                    Control returned = findControlByName(cChild, controlName, controlNamespace);
                    if (returned != null) {
                        return returned;
                    }
                }
            }

            return null;
        }
    }
}

追記:cerego公式のブックマークレットがリリースされています。こっちの方が便利かも。

image

タイトルはホッテントリメーカーで生成したw 無駄に愛愛うるさい気もするが、まぁ気にしない。春じゃないのも気にしちゃいけない。

それはともかく。
iKnowは英語学習用SNSサービスで、英語を学ぶためのe-learningコンテンツと、SNSが融合したサービス。Ruby on RailsとFlashで作成された素晴らしい学習アプリが特徴で、TOEIC向け、TOEFL向けなど様々なコースを、目標日から自動で逆算されたペース配分に従って学習を進めることができる。

さて、そんなiKnowには、「iKnowアイテムバンク」という名前で、登録されている10万語超の英単語を調べることができる辞書機能が存在する。そして、その単語を集めて自分の英単語帳を作ることができる「マイリスト」という機能も存在している。作成したマイリストは、運営元のceregoによって提供されているコースと同じように、iKnowの学習アプリを使ってペース配分しながら勉強することが出来、iKnowを非常に効率的な英語学習プラットフォームとして活用することが出来る。

で、今回はそのマイリスト機能を活用するための基盤となる、iKnowアイテムバンクを検索するための検索プラグインを作成してみた。なお、iKnowアイテムバンクはAjaxを活用しているので、SearchQueryGeneratorは使えないので注意。

OpenSearchプラグイン

Firefox向けOpenSearchプラグイン。以下のリンクからインストールできます。
iKnow ItemBank Searchをインストール(Firefox)

image これを使えば、

  1. 英語ページをブラウズ
  2. 分からない単語に出会う
  3. その単語を検索バーにD&D
  4. 検索結果のページからマイリストに登録
  5. 分からなかった単語リストが出来て、しかもiKnowで復習できるので(゜Д゜)ウマー

となれる。

検索エンジンの切り替えが面倒だ、という場合は、コンテキストメニューから検索できるようにするContext Searchという拡張を入れれば幸せになれるかも知れない。

なおこのOpenSearchプラグインはIEでは使用不能。iKnowが対応していないのか検索結果ページを上手く表示できない。残念…。よってIEコンポーネントブラウザであるSleipnir、Lunascape等も同様。

JUL
26

iKnow再開

Published:2008-07-26 00:32:48 UTC

image

テストも終わってはいないが大体片付いたので、iKnowを再開。TOEFLやら何やらで忙しかったのもあるが、二ヶ月近く放置していたのか…。反省。これからはまた10unit/日のペースに戻してゆきたい。

JUL
24

暑すぎ。

Published:2008-07-24 12:46:00 UTC
かきごおり食べてぇ。 /  /  /  /
JUL
21

iPhoneキター!

Published:2008-07-21 23:10:58 UTC

image

自分が管理しているサークルのサイトに、Google Analyticsを一週間ほど前から導入している。今日ビジターの利用OS一覧を見てみたところ、iPhoneが入っていた。羨ましいぞ持ち主…。

しかしなんだかんだ言ってWindowsは圧倒的だな…。流石。

JUL
19

WordPress 2.6を新規インストールした場合、XML-RPC API経由での投稿機能は、デフォルトでは無効になるようになったらしい。ver.2.6以前からのアップデートの場合は有効のまま。新規インストールしたWordPressにWindows Live Writerなどの投稿クライアントを組み合わせて運用することを考えている場合は注意しましょう。XML-RPC APIの有効化は管理画面の「設定」→「投稿設定」から。以下は情報元からの引用。

XML-RPC is disabled by default for new installs of WordPress. Upgrading will NOT disable XML-RPC (ticket 7157)

WordPress 2.6 – XML-RPC & AtomPub Changes – Joseph Scott’s Blog

このエントリには他にもXML-RPC APIのver.2.51からの変更点(ページテンプレートの取得、Blogオプションの取得/設定が出来るようになった、等)が列挙されているので、このエントリは一見の価値有り。

また、このBlogにはこんな記事もあった。

Custom fields for posts and pages are now exposed and manageable from metaWeblog.getPost/newPost/editPost, wp.getPage/newPage/editPage via the custom_fields field. I was really happy to get this in, I think there is a lot of potential in being able to manage custom fields externally.

WordPresss 2.5 – Joseph Scott’s Blog

WordPress 2.5からはカスタムフィールドのデータをXML-RPC APIから扱えるようになっていたらしい。知らんかった。憶えておこう。

WordPress 2.6の注目の新機能のひとつ、エントリのリビジョン管理機能、WordPress 2.6を入れたついでに早速試してみた。

image

こんな感じに比較が出来る。うん、良い感じ。

次に、WindowsLiveWriterを使用して投稿をした場合を試してみた。

image

これもうまくいく。

だが、WordPressの投稿フォームとWindowsLiveWriterを併用して編集した場合はうまく比較が行われない。

image

WordPressで編集した版はpタグで括られた段落毎にブロックに分けられているのだが、WLWで編集した版は全体が一つのブロックとして纏められてしまっており、それが原因で比較に失敗しているようだ。

一人でBlogを書く際には過去の版に戻す位しかリビジョン管理機能を使うことはないだろうから、あまり影響はないだろうけど、ビジネスブログなどで複数人で記事を推敲する場合はリビジョン比較機能を使う場面も出てくるだろう。そういう場合は投稿クライアントを使うか使わないか統一した方がよさそうだ。(まぁ大した問題じゃないが…。)

JUL
16

WordPress2.6

Published:2008-07-16 11:45:19 UTC

WordPress2.6がリリースされたみたい。日本語版はまだの様子。

WordPress › Blog » WordPress 2.6によれば、一ヶ月スケジュールを前倒しにしてのリリースとのこと。主な新機能としては、

  • 投稿の改訂履歴をwikiのように辿れるようになった
  • 投稿用ブックマークレット
    Youtubeのページで呼び出せば動画をBlogに貼り付け、Fickerのページで呼び出せば画像を貼り付け、などコンテキストに合わせた投稿が可能?
  • Google Gearsを利用し、JavaScriptとCSSをローカルにキャッシュ
  • テーマの適用前プレビュー機能

など。他にも単語数カウント機能など細かい新機能や、194個のBug fixも。ver.2.5とはプラグインやテーマの互換性があるので、特段アップデートする際に注意する点はない、とのこと。

テーマの適用前プレビュー機能は便利そうだけど、WindowsLiveWriterを投稿クライアントに使っている自分にとっては、そこまで気になる機能はないかな。

追記

日本語版がリリースされていたのでアップグレードしてみた。