ブログサイト CMS だと思っていい。時系列ページの扱いに長けていて、固定ページの扱いは面倒。固定ページが10以下でブログ等の時系列ページをたくさん量産したい時には便利。あとは新規記事投稿をカスタマイズして、ポータルサイト的なものも作りやすい。
- 「記事」を「管理画面」から作成して「ユーザ画面」で表示/インデックスできる
- 記事は「カテゴリー」や「タグ」によって分類できる
- 記事の「投稿者」や「購読者」などのアカウント/ロール/認証機能が予め備わっている
- 記事には「コメント」がつけられる
- 記事ではない静的なページを「固定ページ」として登録できる
- WEB サイトとしてのビジュアルや機能について「テーマ」概念で拡張できる
- テーマ内で「メニュー」「ウィジェット」「背景」などの登録をして WEB サイトを構築できる
- 各ベンダーの提供テーマを独自拡張する「子テーマ」を作成できる
- その他
functions.php
など ”カスタマイズ可能なファイル群” によって CSS や PHP などコードレベルで独自拡張できる
- システム的な機能拡張について「プラグイン」概念で拡張できる
- マルチサイト化機能 で 1 サーバ 1 プログラムで複数サイトが構築可能
- ロケール対応 i18n / L10n
基本的には Codex で利用できる API が網羅されているので、やりたいこと別に API を探して云々 ... という形になる。
ロケール対応 i18n / L10n
WordPressの国際化
WordPressの子テーマで翻訳多言語対応する方法 WordPress国際化(i18n)のメモ : WordPress
- テーマファイル内の全フレーバーテキストについて翻訳関数
__()
を通す gettext
ツールで.pot
ファイル ( Portable Object Template: 翻訳リソースファイルテンプレート ) を作成- 上記
.pot
を必要に応じて修正 → リネームして.po
( Portable Object: 翻訳リソースファイル ) を作成 gettext
のmsgfmt
コマンドで.mo
ファイル ( Machine Object: 実翻訳ファイル ) 作成functions.php
でテーマの翻訳ファイル参照先とドメインを設定
# gettext のインストール
$ yum install -y gettext
# 子テーマ配下 PHP を読み取り .pot ファイル作成
$ cd wp-content/themes/my-child-theme
$ mkdir languages
$ find . -iname "*.php" > /tmp/phplist.txt
$ xgettext --from-code=UTF-8 --language=php --keyword=__ --keyword=_e --keyword=_n:1,2 --keyword=_x -f /tmp/phplist.txt -o ./languages/default.pot
$ rm /tmp/phplist.txt
# 上記 default.pot より各言語用 .po ファイル作成
$ cd languages
$ cp -f default.pot ja.po
# .po を編集し .mo へ変換
$ vi languages/ja.po
$ msgfmt --statistics ja.po -o ja.mo
# 子テーマ functions.php で load_theme_textdomain() を after_setup_theme フックに仕込む
$ cd ..
$ vi functions.php
翻訳最上位ドメイン default
( __()
の第二引数で指定するドメイン名のデフォルト値 ) に子テーマ配下の翻訳ファイルを追加すると __('msgid', 'domain')
とか指定しなくてよくなるので楽。
// 子テーマ配下 functions.php で子テーマ内翻訳ファイルを最上位ドメイン default に追加
function add_child_theme_textdomain() {
// e.g. __('my msgid');
load_child_theme_textdomain('default', get_stylesheet_directory().'/languages');
}
add_action('after_setup_theme', 'add_child_theme_textdomain');
wp-content/themes/テーマ
にシンボリックリンク貼れば管理画面からテーマ選択可能。
- WP_Post
- テンプレートより
$post
でアクセス
- テンプレートより
- WP_Query
- テンプレートより
$wp_query
でアクセス - get_queried_object() で現在クエリの
$WP_Post / $WP_Term
など取得可能
- テンプレートより
- WP_Term
WordPressで押さえておきたい!get_posts,WP_Query,query_posts の違いと用例 WordPressで複数のループを使ってカスタム投稿一覧を自在に表示する方法
new WP_Query($args)
や get_posts($args)
でメインループに干渉しないサブループを作成できる。昔は query_posts()
というのもあったがメインループに干渉するため現在非推奨です。それぞれグローバル変数を若干汚染するのでメインループに戻る前に wp_reset_postdata()
でリセット処理が必要なので注意。
$sub_query = new WP_Query(['post_type' => 'post']);
while ($sub_query->have_posts()) : $sub_query->the_post();
the_title();
endwhile;
wp_reset_postdata(); // 必須
アクションフック pre_get_posts - WordPress Codex pre_get_postsを使いこなす!
/*
* カテゴリーページを表示した時に投稿を5件表示する
* @param $query - $wp_query の参照
* @return void
*/
function set_query_for_news($query) {
// アクションフックのタイミングが早いのでかなり慎重におろしていかないと Notice る
if (is_admin()) return;
if (empty(get_queried_object())) return;
if (!$query->is_main_query()) return;
if ($query->is_page('news')) {
if ($query->is_category()) {
// クエリの where 条件を確認
// var_dump($query->query_vars);
// クエリの paged ( ページネーション ) 番号を取得
$paged = get_query_var('paged', 0);
// クエリを初期化
// $query->init();
// クエリの where 条件を追加
$query->set('posts_per_page', '5');
// paged 番号を引き継ぎ
set_query_var('paged', $paged);
}
}
add_action('pre_get_posts', 'set_query_for_news');
Get は基本いつでも可能。
これ対して Set はビュー側 ( テンプレートファイル ) まで下りてきた $wp_query
さんには聞きません。結果セットをイーガーロードせず既に保持した状態で降りてきてます。
get_query_var('page', 1); // 第二引数はなかった時のデフォルト値
set_query_var('page', 2); // セットもあるよ
// 固定ページ
$bool = is_page();
$bool = is_page('about-us');
// ページテンプレート
$bool = is_page_template('about.php');
// カテゴリー/タグ
$bool = is_category('hang-out');
$bool = is_tag('video-game');
// タクソノミー/ターム
$bool = is_tax('brand');
$bool = has_term('tiffany');
// アーカイブ
$bool = is_archive();
// 検索
$bool = is_search();
// 404
$bool = is_404();
// ID
the_ID();
// タイトル
the_title('<h2>', '</h2>');
// カテゴリ
$categories = get_the_category($post->ID);
// アイキャッチ画像
<?php if (has_post_thumbnail()): ?>
<?php the_post_thumbnail('thumbnail') ?>
<?php endif ?>
// ID から
$page = get_post(999);
// フロントページ
$pageID = get_option( 'page_on_front' );
$frontPageObj = get_post( $pageID );
// パスから
$frontPageObj = get_page_by_path('home');
echo get_permalink(80);
echo get_permalink(get_page_by_title('Monthly Events'));
echo get_permalink($post);
echo wp_get_attachment_image( $attachment_id, $size, $icon, $attr );
$images = wp_get_attachment_image_src( $attachment_id, $size, $icon );
$images = wp_get_attachment_metadata( $attachment_id, $unfiltered );
add_filter($filter_hook_name, $function_to_add, $priority, $accepted_args_number);
- show-current-template
- 開発用:現在の適用テンプレートファイルを管理バーへ表示する
- debug-bar
- 開発用:管理バーにデバッグツールを表示
- custom-post-type-permalinks
- カスタム投稿タイプのパーマリンク設定
- tag-groups
- タグにグループを持たせる
- taxonomy-terms-order
- タクソノミ/タームに順序 ( 順番 ) を持たせる
- megamenu
- ドロップダウン式グローバルナビプラグイン
- really-simple-csv-importer
- CSV で記事アップロード
- add-to-any
- SNS シェアボタン追加
- simplicity-add-fields-search-engine
- タクソノミ/タームによる検索を可能にする野良プラグイン
- all-in-one-seo-pack
- Google Analytics をはじめとした SEO 最適化プラグイン
- qTranslate
- Allow Multiple Accounts
- 同じEmailアドレスでユーザーを作成できるようにする
- Google Analytics Dashboard for WP
- ダッシュボードに GoogleAnalytics 連携
- RSS Image Feed
- RSS に画像をのっけるやつ
- Simple Local Avatars
- なんかアバター(右上のやつ)勝手に作ってくれる
- SiteGuard WP Plugin
- とりあえずのセキュリティ対策
- ログイン画面 URL 変更だけは絶対やれ
- WP Admin UI Customize
- ロールによって管理画面をカスタマイズするやつ
- WP Super Cache
- サイトのキャッシュを生成して体感速度向上
- リリース後安定してから稼働させること
- WPFront User Role Editor
- ユーザーに権限を設定してできることを振り分けるやつ
- backWPup
- バックアッププラグイン
- Smart Custom Fields
- カスタムフィールドこねる
- Contact Form 7
- メールフォーム設置プラグイン
- バリデーションは
functions.php
にてフックをしかける - 確認画面設置用の野良プラグイン Contact Form 7 add confirm もあるよ
- add confirm のスクロール制御
- 複数フォームがあるときは
jQuery(this).parents('form').get(0).offsetTop;
でポジションとれた add_filter("wpcf7c_input_error_scroll", '__return_true');
でエラー時もスクロール
複雑な処理向けに 同梱のヘルパー が提供されています。
// ヘルパーのロード ( 単体使用時 )
require_once ABSPATH . 'wp-content/plugins/really-simple-csv-importer/class-rscsv_import_post_helper.php';
// 投稿データの設定
$data = array('post_title' => '投稿タイトル', 'post_content' => '本文', 'post_type' => 'post', 'post_status' => 'publish');
// データベースに追加
$h = RSCSV_Import_Post_Helper::add($data);
// エラーの確認
if ($h->isError()) {
// エラーメッセージの表示
echo implode(' ', $h->getError()->get_error_messages());
} else {
// カスタムフィールドの追加
$meta = array('custom_field_key' => 'Custom Field Value');
$h->setMeta($meta);
// タグの追加
$tags = 'Hello, World';
$h->setPostTags($tags);
// タクソノミーを指定してタームの追加
$terms = array('Lorem', 'Ipsum');
$h->setObjectTerms('category', $terms);
// アイキャッチ画像をアップロード
$image_uri = 'http://example.com/example.png';
$h->addThumbnail($image_uri);
echo 'インポート完了。';
}
/**
* Override Really Simple CSV Importer Class.
* @param string $class
* @return string - Class name.
*/
add_filter('really_simple_csv_importer_class', 'really_simple_csv_importer_class');
function really_simple_csv_importer_class($class) {
if (!class_exists('RS_CSV_Importer')) {
return $class;
}
// ↓ こいつで class RS_CSV_Importer_Override extends RS_CSV_Importer {} して拡張がんばる
require_once "path/to/RS_CSV_Importer_Override.php";
return 'RS_CSV_Importer_Override';
}
/**
* Get attachment ID by URL.
* @param string $url
* @return int $attachment_id - Attachment ID on success, 0 on failure.
*/
function get_attachment_id($url) {
$attachment_id = 0;
$dir = wp_upload_dir();
if ( false !== strpos( $url, $dir['baseurl'] . '/' ) ) { // Is URL in uploads directory?
$file = basename( $url );
$query_args = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'fields' => 'ids',
'meta_query' => array(
array(
'value' => $file,
'compare' => 'LIKE',
'key' => '_wp_attachment_metadata',
),
)
);
$query = new WP_Query( $query_args );
if ( $query->have_posts() ) {
foreach ( $query->posts as $post_id ) {
$meta = wp_get_attachment_metadata( $post_id );
$original_file = basename( $meta['file'] );
$cropped_image_files = wp_list_pluck( $meta['sizes'], 'file' );
if ( $original_file === $file || in_array( $file, $cropped_image_files ) ) {
$attachment_id = $post_id;
break;
}
}
}
}
return $attachment_id;
}
CSV ファイルのヘッダー ( フィールド名 ) の接頭辞に scf_
つけてあげれば勝手に連携する。が、複数値をカンマ区切りで受け付けられない。
サムネイルは「メディアの ID または URL」で自動取得されるが、カスタムフィールド系の画像は ID しか受け付けない。アドオン RS CSV Importer Media Add-On で一応 URL を受け付けるようになる。が、複数値をカンマ区切りで受け付けられない。
拾い物の get_attachment_id()
と組み合わせて頑張ってみた。
/**
* Save meta filter for Really Simple CSV Importer.
* @param array $meta
* @param array $post
* @param bool $is_update
* @return array $meta
*/
add_filter('really_simple_csv_importer_save_meta', function($meta, $post, $is_update) {
foreach ($meta as $key => $value) {
// Optimize for multiple values of Smart Custom Fields.
if (mb_strpos($key, 'scf_') !== false) {
if (mb_strpos($value, ',') !== false) {
$metaValues = explode(',', $value);
$meta[$key] = $metaValues;
}
}
// Convert to media-id from media url.
if (preg_match('/.*(_file|_img)$/', $key)) {
if (is_array($meta[$key])) {
foreach ($meta[$key] ?? [] as $metaKey => $metaValue) {
if (parse_url($metaValue, PHP_URL_SCHEME)) {
$meta[$key][$metaKey] = get_attachment_id($metaValue);
}
}
} else {
if (parse_url($meta[$key], PHP_URL_SCHEME)) {
$meta[$key] = get_attachment_id($meta[$key]);
}
}
}
}
return $meta;
}, 10, 3);
WordPress の一般的なテーマ ( インストールされた直後の標準テーマも含めて ) は jQuery 依存しており 1 系の jQuery を内部的に利用している。このとき、多くのテーマは開発者に対して jQuery のバージョンを強制しない目的で $
を JavaScript グローバルにしていない。よってテーマ組込の jQuery をそのまま利用する場合は、以下のように jQuery を直接呼び出す必要がある。
<script>
jQuery(document).ready(function(){
console.log('hoge')
})
</script>
function pass_args($attr) {
return "引数は{$attr[0]}でした。";
}
add_shortcode('pass_args', 'pass_args');
[pass_args hoge] // 引数はhogeでした
/**
* Hello short code.
* @param string $name
* @return string $html
*/
add_shortcode('hello', 'hello');
function hello($args) {
echo 'Hello '.$args['name'].PHP_EOL;
}
[hello name="bob"] // Hello bob
//example.com/?order=ASC
//example.com/?order=ASC&orderby=title
// functions.php
/**
* ログインフォーム出力ショートコード
* ( 固定ページ出力想定 / Site Guard CAPTCHA 対応 )
* @param void
* @return string $html
*/
add_shortcode('print_login', 'print_login');
function print_login() {
ob_start();
if (is_user_logged_in()) { ?>
<div class="card mb-5">
<div class="card-header">ユーザログイン</div>
<div class="card-body">
<h5 class="card-title">既にログインしています</h5>
<p class="card-text">ログアウトする場合は「ログアウト」ボタンを押下してください。</p>
<a class="btn btn-primary" href="<?php echo wp_logout_url().'?redirect_to='.home_url() ?>'">ログアウト</a>
</div>
</div>
<?php } else { ?>
<div class="card mb-5">
<div class="card-header">ユーザログイン</div>
<div class="card-body">
<form method="post" action="<?php echo wp_login_url().'?redirect_to='.home_url()?>'">
<dl class="row">
<dt class="col-md-2">ユーザー名</dt>
<dd class="col-md-10">
<input type="text" class="form-control" name="log" id="login_username" value="">
</dd>
<dt class="col-md-2">パスワード</dt>
<dd class="col-md-10">
<input type="password" class="form-control" name="pwd" id="login_password" value="">
</dd>
<dt class="col-md-2">画像認証</dt>
<dd class="col-md-10 pt-3">
<?php
$SiteGuard = new SiteGuard_CAPTCHA(); // SiteGuard プラグインのキャプチャ呼び出し
$SiteGuard->handler_login_form(); // handler_***_form() メソッドで各種フォーム呼び出し
?>
</dd>
<dt class="col-md-2"></dt>
<dd class="col-md-10 text-right">
<input class="btn btn-primary" type="submit" value="ログイン" />
</dd>
</dl>
</form>
</div>
</div>
<?php }
$html = ob_get_contents();
ob_end_clean();
echo $html;
}
Smart Custom Fields はカスタムフィールドを管理するシンプルなプラグインです。 Smart Custom Fields - wordpress.org
- フィールドグループの繰り返し対応。
- メタデータのリビジョン対応。
- メタデータのプレビュー対応。
// Get post meta in loop
SCF::get('custom-field-name');
// Get post meta out loop
SCF::get('custom-field-name', $post->ID);
// Get user meta
SCF::get_user_meta($user_id, 'custom-field-name');
// Get term meta
SCF::get_term_meta($term->term_id, $term->name, 'custom-field-name' );
前提として /my_category/category_a
のようにアーカイブを呼び出せるような状態で ... /my_category/category_a/my_tag/tag_b
みたいなルーティングでアーカイブに渡したいができないみたい。
この場合は Querystring で /my_category/category_a?my_tag=tag_b
みたいに渡してあげると WP_Query が条件を絞り込んだ状態でアーカイブにセットされるようです。
で、渡ってきたクエリ条件は get_query_var()
で取得可能。ちなみに set_query_var()
であとからセットもできるよ。
// TEMPLATE: archive.php
// URL: /my_category/category_a?my_tag=tag_b
echo get_query_var('my_tag', ''); // tag_b
開発中には、エラー(notice)を表示するように、デバッグモードにすることができる。
# wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', true); // wp-content/debug.log への書き出し
WordPress では以下のようなデータ構造になっている。
テーブル名 | 対応データ | 開発上の分類 |
---|---|---|
wp_options |
管理画面 > 一般設定など | 【定数】に近い |
wp_term_* |
投稿タイプ/タクソノミーなど | 【スキーマ = テーブル定義】に近い |
wp_posts / wp_terms / wp_users |
投稿/画像 ( メディア ) /ターム/ユーザなど | 【ジャーナル = 履歴】に近い |
wp_*meta |
投稿のカスタムフィールドやユーザの権限情報など | 上記【ジャーナル = 履歴】に付随するメタ情報なので【ジャーナル】に近い |
wp_${プラグイン名} |
プラグインの設定値や履歴情報など | プラグインの DB 構造によって【スキーマ】にも【ジャーナル】にもなり得る |
Git などでの複数人開発時には、管理画面から行った様々な変更は全て DB 変更となるため ...
- 管理画面の設定手順を世代管理し更新していく
- 常に最新の DB 全体ダンプを「真」としてこれを世代管理していく
- このとき、DB の更新 ( 管理画面からの入力 ) を行えるのは常に 1 人だけとする
上記何れかのパターンで開発を進行させるしかない。
- プロビジョンにて DB 全 DROP → 必要なデータのみ再生成 を仕込む
- ブランチのチェックアウトでは「必ず上記プロビジョン (
vagrant reload
) による DB の初期化を行う 」こととする - メインコミッターは「管理画面からのデータ変更が必要な作業」全てを受け持つ
- メインコミッターは「管理画面からのデータ変更が必要な作業」を行った場合、DB ダンプファイルを更新する
- ダンプファイルは「全テーブルの構造 + データ」( 全 DROP なので )
- 出荷時に必要な画像類が増えた場合は
.gitignore
にて無視から外す - 前提として出荷時に不要な
wp-content/uploads
配下の画像ファイルなどは無視されていることとする
- メインコミッター以外は「管理画面からのデータ変更が必要な作業を行わない = ソースコード変更のみ」とする
- メインコミッター以外は「次回
vagrant up
までの一時データ」としてしか「管理画面からの入力」を行わない
WP を内包するサイト内で wp-header.php
( 違ったかも ? ) を require_once()
すると API 使えるようになるみたい。ブログ機能だけを分離して配下にかかえたサイト ( http://example.com/blog
この /blog
に WordPress が入ってるとき ) とか、この手法で /blog
配下以外で WordPress の記事データを楽に扱える。
WordPressでエディタに自動生成されるタグ(<p>とか<br>)を制御する方法まとめ
/**
* Validations for Contact Form 7.
* @param array $result
* @param array $tags
* @return array $result
*/
add_filter('wpcf7_validate', 'wpcf7_validate_customize', 11, 2);
function wpcf7_validate_customize($result, $tags) {
$emailInputName = 'your-email';
$emailConfirmInputName = 'your-email-confirm';
$email = '';
$emailConfirm = '';
foreach ($tags ?? [] as $tag) {
$type = $tag['type'];
$name = $tag['name'];
if (empty($_POST[$name])) continue;
$post = trim(strtr((string)$_POST[$name], "\n", ""));
switch ($type) {
case 'email':
case 'email*':
if ($name == $emailInputName) $email = $post;
if ($name == $emailConfirmInputName) $emailConfirm = $post;
break;
case 'tel':
case 'tel*':
if (preg_match( '/^[0-9]*$/', $_POST[$name]) == false) {
$result->invalidate($name, 'ハイフンは省略し数字のみでご記入ください。');
}
break;
}
}
if ($email != $emailConfirm) $result->invalidate('your-email', '確認用メールアドレスの入力に誤りがあります。');
return $result;
}
野良プラグイン Contact Form 7 add confirm
http://www.butlerblog.com/2013/12/12/easy-smtp-email-wordpress-wp_mail/
// functions.php
/**
* This function will connect wp_mail to your authenticated
* SMTP server. This improves reliability of wp_mail, and
* avoids many potential problems.
*/
add_action('phpmailer_init', 'send_smtp_email');
function send_smtp_email($phpmailer) {
if (WP_DEBUG) {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.example.com';
$phpmailer->SMTPAuth = false;
$phpmailer->Port = '25';
$phpmailer->Username = '';
$phpmailer->Password = '';
$phpmailer->SMTPSecure = '';
$phpmailer->From = 'no-reply@example.test';
$phpmailer->FromName = 'no-reply@example.test';
} else {
$phpmailer->isSMTP();
$phpmailer->Host = 'smtp.example.com';
$phpmailer->SMTPAuth = true;
$phpmailer->Port = '25';
$phpmailer->Username = 'username';
$phpmailer->Password = 'password';
$phpmailer->SMTPSecure = '';
$phpmailer->From = 'no-reply@example.com';
$phpmailer->FromName = 'no-reply@example.com';
}
}
WordPress は世界的に広く利用されている CMS なことに加えて、インストール直後の初期設定状態が脆弱で攻撃の対象になりやすい。
本体のセキュリティアップデートは比較的頻繁に行われている。プラグインは開発者・利用者が多くアップデートの頻繁なものを選択するべき。
アップデートによりベンダーファイルは随時更新されてしまうので、カスタマイズの際は「子テーマ」などカスタマイズ用に用意されている枠の中で行うべき ( ベンダーファイルへの直接編集は NG ) 。
echo esc_html($post->post_content);
WordPress は初期状態だと /admin
や /login
でログインページ ( wp-login.php
) へリダイレクトされる。攻撃者はアプリケーションが WordPress を利用したサイトだと確認した場合、前述した特徴から容易にログインページへ遷移し総当たり攻撃を仕掛けられる。
SiteGuard WP Plugin などを利用して、ログインページの URL の変更と上記リダイレクトさせない設定にするべき。
DB のユーザ名/パスワードなどを記載することになる wp-config.php
は普通にインストールすると Public な領域に設置されるため 必ず施策を行うべき。 WordPress 2.9 移行は「一つ上の階層に移動させる」ことが可能 ( WordPress 本体が探しに行ってくれる ) 。しかし上の階層がドキュメントルート配下なら意味がないので、別途 .htaccess
などでファイル参照をブロックすること。
# .htaccess
<files wp-config.php>
order allow,deny
deny from all
</files>
注: 下記のコードが WordPress によって上書きされないようにするためには、.htaccess ファイルの # BEGIN WordPress と # END WordPress タグの外側に書き込んでください。この2つのタグの間に何かを書き込むと WordPress が上書きしてしまうことがあります。 - WordPress Codex
.htaccess
でリダイレクトルールを設定。
# Block the include-only files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
# BEGIN WordPress
WordPressを踏み台にするPingback機能を悪用したDDoS攻撃への対応方法 WordPressのxmlrpc.phpを無効にして不正アクセス対策 wordpressのxmlrpc.phpに対するブルートフォースアタックを防ぐ
functions.php
で xmlrpc.php
を無効化して ...
add_filter('xmlrpc_enabled', '__return_false');
.htaccess
でリダイレクトルールを設定。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^xmlrpc\.php$ "http\:\/\/0\.0\.0\.0\/" [R=301,L]
</IfModule>