WPCustomFieldsEditor Archive

APR
17

WPCustomFieldsEditor更新

Published:2009-04-17 17:13:29 UTC

動かなくなったまま放置していたWPCustomFieldsEditor、修正したものと差し替えました。WPCustomFieldsEditorは、その名の通り、WordPressのカスタムフィールドを修正するためのWindows Live Writerのプラグインです。

Windows Live Writerがアップデートしたら動かなくなっていたのは、横着してWLWの内部的なアセンブリを直接参照した時点でまぁ予見出来た話ではあるのですが・・・。今回はちゃんとリフレクションで書き直しております。また、WordPressとのやり取りも、WP-XML-RPCLib2で行うように変更しました。

WPCustomFieldsEditor.zip

ソースコード:WPCustomFieldsEditor.sln.zip

OCT
2

WPCustomFieldsEditor書き直し

Published:2008-10-02 01:18:04 UTC

先日WPCustomFieldsEditorが正常に動作しないというコメントがあり、良い機会なのでBugFixも兼ねてWordPressとXML-RPC APIを通じて遣り取りを行う部分を書き直してみました。これまでは文字列連結でリクエスト用のXMLを捻り出してなんとか凌いでいたのですが、WindowsLive.Writer.CoreServices.dllにXML-RPC Clientライブラリがあるのを見つけたので、今回はそのお世話になることに。WLWのプラグインを作る分には怒られないんじゃないかな・・・?

で、以下が書き直した後のOnPostPublishメソッドのソースコード。

        public override void OnPostPublish(IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish) {

            PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);
            Blog blog = new Blog(postEditorForm.CurrentBlogId);
            PropertyInfo propInfo = blog.GetType().GetProperty("BlogClient", BindingFlags.NonPublic | BindingFlags.Instance);
            IBlogClient blogClient = (IBlogClient)propInfo.GetValue(blog, null);

            WordPressClient xmlRpcClient = blogClient as WordPressClient;
            if (xmlRpcClient != null && !publishingContext.PostInfo.IsPage) {

                const string userAgent = "WPCustomFieldEditor";

                XmlRpcString postId = new XmlRpcString(publishingContext.PostInfo.Id);
                XmlRpcString userName = new XmlRpcString(xmlRpcClient.Username);
                XmlRpcString password = new XmlRpcString(xmlRpcClient.Password);

                HttpRequestFilter filter = delegate(System.Net.HttpWebRequest request) {
                };
                XmlRpcClient client = new XmlRpcClient(blog.PostApiUrl, userAgent, filter, xmlRpcClient.Options.CharacterSet);
                XmlRpcMethodResponse res = client.CallMethod("metaWeblog.getPost", postId, userName, password);
                if (res.FaultOccurred) {
                    showErrorMessage(dialogOwner, res.FaultString);
                    return;
                }

                XmlNodeList customFieldsNodes = res.Response.SelectNodes("//struct/member[name="custom_fields"]/value/array/data/value/struct");

                CustomField[] customFields = new CustomField[customFieldsNodes.Count];
                for (int i = 0; i < customFieldsNodes.Count; i++) {
                    XmlNode item = customFieldsNodes[i];
                    customFields[i] = new CustomField();
                    customFields[i].Id = item.SelectSingleNode("member[name="id"]/value/string").InnerText;
                    customFields[i].Key = item.SelectSingleNode("member[name="key"]/value/string").InnerText;
                    customFields[i].Value = item.SelectSingleNode("member[name="value"]/value/string").InnerText;
                }

                WPCustomFieldsEditor fieldEditor = new WPCustomFieldsEditor(customFields, this.Options);
                DialogResult result = fieldEditor.ShowDialog(dialogOwner);
                if (result == DialogResult.OK) {

                    Func getRpcMemberValue = delegate(string name) {
                        return res.Response.SelectSingleNode("//struct/member[name="" + name + ""]/value/*").InnerText;
                    };

                    XmlRpcStruct[] cv = new XmlRpcStruct[fieldEditor.CustomFields.Length];
                    for (int i = 0; i < cv.Length; i++) {
                        CustomField sourceItem = fieldEditor.CustomFields[i];
                        List<XmlRpcMember> structMember = new List<XmlRpcMember>();
                        if(!string.IsNullOrEmpty(sourceItem.Id)){
                            structMember.Add(new XmlRpcMember("id", sourceItem.Id));
                        }
                        if(!string.IsNullOrEmpty(sourceItem.Key)){
                            structMember.Add(new XmlRpcMember("key", sourceItem.Key));
                        }
                        structMember.Add(new XmlRpcMember("value", sourceItem.Value));
                        cv[i] = new XmlRpcStruct(structMember.ToArray());
                    }

                    XmlRpcStruct content = new XmlRpcStruct(new XmlRpcMember[]{
                        new XmlRpcMember("title",getRpcMemberValue("title")),
                        new XmlRpcMember("description",getRpcMemberValue("description")),
                        new XmlRpcMember("mt_text_more",getRpcMemberValue("mt_text_more")),
                        new XmlRpcMember("mt_allow_comments",getRpcMemberValue("mt_allow_comments")),
                        new XmlRpcMember("mt_allow_pings",getRpcMemberValue("mt_allow_pings")),
                        new XmlRpcMember("mt_keywords",getRpcMemberValue("mt_keywords")),
                        new XmlRpcMember("wp_slug",getRpcMemberValue("wp_slug")),
                        new XmlRpcMember("wp_password",getRpcMemberValue("wp_password")),
                        new XmlRpcMember("wp_author_id",getRpcMemberValue("wp_author_id")),
                        new XmlRpcMember("mt_excerpt",getRpcMemberValue("mt_excerpt")),
                        new XmlRpcMember("mt_keywords",getRpcMemberValue("mt_keywords")),
                        new XmlRpcMember("custom_fields",new XmlRpcArray(cv))
                        });
                    XmlRpcBoolean isPublish = new XmlRpcBoolean(publish);
                    
                    res = client.CallMethod("metaWeblog.editPost", postId, userName, password, content, isPublish);
                    if (res.FaultOccurred) {
                        showErrorMessage(dialogOwner, res.FaultString);
                        return;
                    }
                }


            }


        }

WLWによるエントリの投稿が終わった後に呼び出され、サーバーからその投稿されたエントリのデータを取得し(20行目付近)、それに基づいてカスタムフィールドのデータを付加したエントリのデータを作成して投げ直すという流れ。

AUG
13

WPCustomFieldsEditorよもやま噺

Published:2008-08-13 19:07:33 UTC

このエントリではWPCustomFieldsEditorを製作するにあたって蓄えたトリビア(死語?)紹介をしてみます。後半愚痴だか雑記だかよく分からなくなっていますが。 ん?二度ネタが多い?それは仕様です。

ログイン情報の取得方法

WPCustomFieldsEditorではXML-RPCを使ってWordPressを操作しています。WordPressをXML-RPCで操作するには、WordPressのエンドポイントURLとログインするためのユーザー名とパスワードが必要となります。これらは、リフレクションなどを使って、Windows Live Writer(以下WLW)が内部に保持している情報を取得しています。エンドポイントURLはBlogクラスのPostApiUrlプロパティから、ユーザー名とパスワードはXmlRpcBlogClientクラスのUsernameプロパティ、Passwordプロパティからそれぞれ取得できます。これらのクラスのインスタンスの取得方法については、SharpLab. – 中まで見よう!Windows Live Writerというエントリに書きましたのでそちらを参照してください。

WP-XML-RPC APIの情報の集め方

WordPressをXML-RPCで操作していると述べましたが、WordPressにはmetaWeblog APIを拡張したWP-XML-RPC APIがあり、これを使用しています。metaWeblog APIについてはMovableType で使える XML-RPC APIというページが詳しいです。WordPressが独自に拡張した部分についてはXML-RPC wp « WordPress Codexに纏まっています。

ただ、これだけでは残念ながらWordPressのXML-RPCについて網羅しきれていません。最近新しく追加された機能などについて調べる場合はwp-xmlrpc Info PageからMLのアーカイブをさらう必要があります。

それでも良く分からなかったら?幸いWordPressはOSSです。ソースコードを読みましょう。その際、便利なのがこちら、PHPXref 0.7: WordPress (latest release)。PHPXrefというツールで、クロスリファレンス化したWordPressのソースコードが掲載されています。

WP-XML-RPC APIによるカスタムフィールドの扱い方

[wp-xmlrpc] exposing and manipulating custom fieldsに記述があります。大体分かり易い記述ですが、ちょっと躓くところがあったので記しておきます。以下の部分。

If a custom field entry included with a post contains an "id" value then an update is done. If there is no "id" then an add is done. If there is an "id" value and no "key" value then the custom field with that id is deleted.

「なになに…idがなければ新しいカスタムフィールドとして追加され、idはあってもkeyがなければそのカスタムフィールドは削除されるとな?」と理解し、追加したい場合はidに空文字列を持たせる形で実装を進めたのですが、うんともすんとも動かない。カスタムフィールドを削除しようと思ってkeyに空文字列を渡した場合には空文字列をキーにしたカスタムフィールドが出来てしまうという始末。(管理画面からは空文字列をキーにすることは出来ないのでバグでしょう。)

実はno idという状態とはidの値を空にしろ、という意味ではなく、idというメンバ自体を持たせるな、ということでした。今振り返ってみて素直に読めば確かにその方が読んでいて通りが良いですが、なんだかなー、もーちょっとわかりやすくしてくれよ、と思いました、はい。

アンダーバーで始まるフィールド

SharpLab. – WPCustomFieldsEditor制作中というエントリにも書いたのものですが。

WordPressの管理画面からは見えないのですが、WP-XML-RPC API経由だと_edit_last、_edit_lockや、_encloseme、_pingmeなどの謎のカスタムフィールドが見える場合があります。これらアンダーバーでキー名が始まるカスタムフィールドですが、結局ググってもよくわからなかったので、WPCustomFieldsEditorでは一律無視することにしています。従って、ユーザーがアンダーバーでキー名が始まるカスタムフィールドを作成した場合も巻き添えで無視されてしまうので、運用には十分注意してください。

AUG
13

WPCustomFieldsEditor

Published:2008-08-13 11:18:23 UTC

これは何?

Windows Live WriterにWordPressのカスタムフィールド編集機能を追加するプラグインです。カスタムフィールドはエントリに独自のメタデータを付加したい場合のために確保されたフィールドで、WordPressをCMSとして多少手の込んだサイト管理に使用する場合には非常に重宝する機能です。このプラグインでは、エントリに付加するカスタムフィールドを、投稿時に編集する機能をWindows Live Writerに提供します。

ダウンロード

WPCustomFieldsEditor.zip

動作環境

  • Microsoft Windows XP SP2 以降または Windows Vista(Windows Live Writerに準拠)
  • Microsoft .NET Framework 2.0以上
  • Windows Live Writer Beta(Build14.0.5025.904)
  • WordPress 2.5以上

※現在、Writer Zoneで配布されているTechnical Preview版では、おそらく動作しなくなっています。ご注意ください。

インストール方法

ダウンロードしたファイルを展開してsetup.exeを実行してください。

アンインストール方法

コントロールパネルのプログラムの追加と削除からWPCustomFieldsEditorを選択し削除してください。

操作説明

image エントリの投稿を行うと、スプラッシュウィンドウに続いてカスタムフィールドの編集画面が現れます。この画面からカスタムフィールドの追加・削除・編集を行うことが出来ます。


image

更新履歴

  • 2008-10-01 ver.1.03に差し替え。内部動作を変更。
  • 2008-09-20 ver.1.02に差し替え。WLW Beta(Build14.0.5025.904)に対応。
  • 2008-08-13 Blogシステムの判別ミスがあったのでver.1.01に差し替え
  • 2008-08-13 イニシャルリリース
AUG
10

WPCustomFieldsEditor制作中

Published:2008-08-10 01:45:00 UTC

image 

WordPressのカスタムフィールドをWindowsLiveWriterから編集するためのプラグインを製作中。プラグインの形式はpublish notification hooksプラグイン。とりあえずXML-RPCを使って記事に付加されたカスタムフィールドの一覧を取得するところまで漕ぎ着けた。ちなみにXML-RPCでWordPressを操作する際に必要となるID、PasswordにはSharpLab. – 中まで見よう!Windows Live Writerというエントリで紹介した方法を使用してWLWが内部に保持している情報を取得して使っています。エンドポイントURLも確かBlogクラスあたりのメンバにあったのを使用。

さて、ここまででカスタムフィールドの仕様に関して気づいたことが何点か。

  • _edit_last、_edit_lockや、_encloseme、_pingmeなどの謎のカスタムフィールドがWordPressによって勝手に追加されている。アンダーバーが付いているカスタムフィールドはWordPressによって自動的に追加されたもの?ちなみにこれらはWordPressの管理画面からは不可視。
  • カスタムフィールドのキーは一意である必要はない。キーとは別にidが振られており、それで管理が行われている。(参考:[wp-xmlrpc] exposing and manipulating custom fields

来週暇なんで作業が進むといいなー。