マルチサイトで参加サイト一覧を新しい順に並べる | よくあるWordPress

マルチサイトにした場合「ダッシュボード → 参加サイト」から管理するサイトが一覧されます。

サイト一覧

こちら、サイトが作成された順になっているので、古いサイトが上に来てしまいます。
これを新しい順に並び替えます。

functions.php かプラグインで下記のようにします。

1
2
3
4
5
function my_sites_order() {
    global $blogs;
    $blogs = array_reverse( $blogs );
}
add_action( 'myblogs_allblogs_options', 'my_sites_order' );

これで並び替え完了です。

参加サイトを新しい順に並べる

– 2014/02/14追記 –
admin bar のサイト一覧も変えたいって場合は、下記のようにします。

1
2
3
4
function my_sites_order( $blogs ) {
    return array_reverse( $blogs );
}
add_action( 'get_blogs_of_user', 'my_sites_order' );

admin bar のサイト一覧も並べ替え
– 追記おわり –

ついでに、一覧のメニュー(表示 | ダッシュボード)の後にリンクを追加するには myblogs_blog_actions アクションを使用します。

1
2
3
4
function my_sites_blog_link( $link ) {
    return $link . ' | <a href="#">TEST</a>';
}
add_action( 'myblogs_blog_actions', 'my_sites_blog_link' );

サイト一覧にメニュー追加

カスタムポストのパーマリンクを投稿IDにする / よくあるWordPress

カスタムポストを利用するとパーマリンクがイマイチな感じになるので、試行錯誤してみました。
WordPress3.5 で、ブログサイトではなくビジネスサイトのCMSとして使用する前提です。
試したのはマルチサイトでしたが、そうでなくても大丈夫と思います、たぶん。

カスタムポストを使用するとパーマリンクは /custom_post_name/slug のようになります。
これだとお客さんとかに使ってもらう時にうっかりタイトル日本語で入れるとイロイロ面倒です。
管理画面からパーマリンクの設定を変更していたりするとスラッグの書き換えもできなくなったりしますし。

これを通常の投稿のように post_id で出来ないかとイロイロ調べていたら下記ページが見つかりました。
http://memocarilog.info/wordpress/theme-custom/3909
http://webpaprika.com/387.html

ただ、$wp_rewrite ルールの中を見ると既にイロイロ登録されているみたいです。
なので、参考にしつつもなるべく余計なルールを追加しない方向で検討しました。

まずは通常の投稿設定

カスタム投稿の前に通常の投稿を /blog のように使いたいので、管理画面の「設定 → パーマリンク設定」から、カスタム構造で「/blog/%post_id%」とします。

固定ページで blog ページをつくっておいて「設定 → 表示設定」からフロントページの表示を固定ページに、投稿ページを先ほどの blog ページにしておきます。

ここまではカスタムポスト関係ないんですが、後で関連してきます。

カスタムポストを登録する

試しにお知らせを news として登録する場合は、下記のように function.php に記述するかプラグイン作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function custom_post_types() {
    $label = 'お知らせ';
    $slug = 'news';
    $option = array(
        'label' => $label,
        'labels' => array(
            'name' => $label,
            'singular_name' => $label,
            'add_new_item' => $label . 'を追加',
            'add_new' => '新規追加',
            'new_item' => '新規' . $label,
            'view_item' => $label . 'を表示',
            'not_found' => $label . 'は見つかりませんでした',
            'not_found_in_trash' => 'ゴミ箱に' . $label . 'はありません。',
            'search_items' => $label . 'を検索',
        ),
        'capability_type' => 'post',
        'menu_position' => 5,
        'rewrite' => array(
            'slug' => $slug,
            'with_front' => false
        ),
        'public' => true,
        'query_var' => true,
        'has_archive' => true,
        'hierarchical' => false,
        'supports' => array('title', 'editor'),
    );
    register_post_type('news', $option);
}
add_action('init', 'custom_post_types');

register_post_type の説明はリファレンス見て下さい。

ココでのポイントは ‘rewrite’ オプションですかね。
with_front を false にしないと、先のパーマリンク設定の影響を受けて /blog/news となってしまいます。
あと、’public’ オプション忘れるとページが見れなかったり、’has_archive’ がないと一覧ページがでなかったりします。

で、実はこの時点で /news から一覧ページ、/news/slug でパーマリンクが見えるので細かいこと気にしないという方はそれでもよかったりします。
ちょっと検索するとこの部分でも rewrite ルールの設定が必要という情報出てきますが、3.5からなのか特に設定しなくても大丈夫です。

2013.02.02追記
内容変更するたびに「設定 → パーマリンク設定」から変更を保存しないと反映されません。
自分もファイルアップしてから、ああそうだったと気づくことが多かったです。

パーマリンクを投稿IDに設定する

ここでようやく本題ですが、気にしないと言いつつも、facebook でシェアした時にURLの見栄えがよろしくないのは気になると思います。
日本語だとURLエンコードされちゃいますし。

そこでひかかったのですが、既に rewrite ルールが設定してあるので、先に紹介した参考サイトのように新たにルールを追加しても、適用済みのルールが優先されるので NOT FOUND になってしまいます。
※パーマリンクを POST ID にしたので、一致するスラッグがないからだ思われます

なので登録済みのルールを変更することにします。
パーマリンクの設定はカスタムポスト名(この場合 ‘news’)で登録されているので、参考サイトのようにスラッグの部分を投稿IDに変更します。

1
2
3
4
5
6
7
8
9
10
11
function myposttype_permalink($post_link, $id = 0, $leavename) {
    global $wp_rewrite;
    $post = &get_post($id);
    if ( is_wp_error( $post ) )
        return $post;
    $newlink = $wp_rewrite->get_extra_permastruct($post->post_type);
    $newlink = str_replace('%'.$post->post_type.'%', $post->ID, $newlink);
    $newlink = home_url(user_trailingslashit($newlink));
    return $newlink;
}
add_filter('post_type_link', 'myposttype_permalink', 1, 3);

リライトルールは既に追加してあるのですが、get_extra_permastruct のように取得する方法はどうもないみたいです。
ただ、ソース見た感じ、既にあるものは上書きしてくれるみたいです。
なので、そのまま %news% を置き換えることにします。

1
2
3
4
5
function myposttype_rewrite() {
    global $wp_rewrite;
    $wp_rewrite->add_rewrite_tag('%news%', '([0-9]+)', 'post_type=news&p=');
}
add_action('init', 'myposttype_rewrite');

参考サイトにある add_permastruct は既に登録済みなので add_rewrite_tag で上書きするだけでOKです。
以上で、ビジネスサイトとしてだいぶ理想的なURLに設定できたと思います。
マルチサイトに対応してるのがありがたかったです。

SpriteSheetでキャラクタアニメーション / CreateJS

CreateJSではSpriteSheetというアニメーション用に用意されたクラスがあります。
この場合はパラパラ漫画的なアニメーションです。

キャラクタのアニメーションをずらっと並べた画像を用意します。
サンプルにある下記のような感じ。
アニメーションGIFとかじゃなく、全ての動きが並んだ状態です。
https://github.com/CreateJS/EaselJS/blob/master/examples/assets/runningGrant.png

そして、下記のようにアニメーションの設定をします。
設定方法は何種類か用意されていて、細かい設定が出来るようになってます。
http://createjs.com/Docs/EaselJS/SpriteSheet.html

1
2
3
4
5
6
7
8
9
10
11
12
var playerWidth = 165;
var playerHeight = 292;
var spriteSheet = {
    images: ['path/to/image.png'],
    frames: { width: playerWidth, height: playerHeight },
    animations: {
        run: [0, 25],
        jump: [26, 63]
    }
};
var ss = new createjs.SpriteSheet(spriteSheet);
var character = new createjs.BitmapAnimation(ss);

この場合、アニメーション名「run」に 0 〜 25 までの連続した動きが使われます。
frames で指定した width, heigt で自動的に分割されます。
0 〜 25 はこの分割された配列のインデックスみたいなもんです。

「jump」のあとはそのまま「run」を再生させたり、「run」はルーブさせたりできます。

1
2
ss.getAnimation("run").next = "run";
ss.getAnimation("jump").next = "run";

キャラクタはいったん Container に登録してから使った方が後からイロイロ調整できそうです。
というか、addChild が Container にしか用意されてないので、そうしろってコトかと。

キャラクタの移動は Container で調整したいので、画像の中心に座標の原点が来るようにズラします。
移動や回転、拡縮に便利なので。

1
2
3
var player = new Container;
player.addChild(character);
character.setTransform(-playerWidth/2, -playerHeight/2);

あとはキーイベントなどに関連付けたりして、アニメーションを再生させればOKす。
再生は設定で指定したアニメーション名を使用します。

1
character.gotoAndPlay('run');

Error: Suspended

横スクロール / CreateJS

シューティングみたいな横スクロールつくってみる。
背景とか画像読み込むので PreloadJS も使う。

1
<script src="http://code.createjs.com/preloadjs-0.2.0.min.js"></script>
1
2
3
4
5
6
var manifest = [
    { src: "img/bg.png", id: "background" },
    { src: "img/fg.png", id: "foreground" }
];
var loader = new createjs.PreloadJS;
loader.loadManifest(manifest);

画像の読み込み完了してから各処理に移りたいので、下記のようにしておく。

1
2
3
4
5
6
7
8
9
10
11
12
13
var assets = [];

loader.onFileLoad = handleFileLoad;
loader.onComplete = handleComplete;

function handleFileLoad(e){
    assets.push(e);
}
function handleComplete(){
    for (var i = 0; i < assets.length; i++){
        // 読み込まれた画像を処理する
     }
}

onComplete 時には assets に読み込んだ画像データが格納されています。
画像もこれまでの円や四角と同じで Shape で作成します。

1
2
3
4
5
6
for (var i = 0; i < assets.length; i++){
    var result = assets[i].result;
    var s = new createjs.Shape;
    s.graphics.beginBitmapFill(result).drawRect(0, 0, result.width, result.height);
...
}

あとはオブジェクトごとに tick でそれぞれ動きをつければそれらしくなります。
画像を複数枚用意してアニメーションさせることも出来るみたいです。
今回は簡単に座標の移動だけやってみました。

Error: Suspended

Segmentで多関節 / CreateJS

CreateJS使って多関節やってみます。
サンプルのSegment参考にしてます。

多関節は描画が簡単なので円にします。
Shape だと親子階層出来ないので、Container 使います。

1
2
3
4
var container = new createjs.Container();
var shape = new createjs.Shape();
shape.graphics.f(color).dc(0, 0,radius);
container.addChild(shape);

CreateJS のサンプルでは下記のように Container クラスを継承して使ってました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Segment(){
    this.r = radius;
    this.color = 'yellow';
    this.init();
}
Segment.prototype = new createjs.Container();
Segment.prototype.r;
Segment.prototype.color;
Segment.prototype.Container_initialize = Segment.prototype.initialize;
Segment.prototype.init = function(){
    this.Container_initialize();
    this.addChild(this.getSprite());
};
Segment.prototype.getSprite = function(){
    var s = new createjs.Shape();
    s.graphics.f(this.color).dc(0, 0, this.r);
    return s;
};

あとはいくつか親子階層にしてみます。

1
2
3
4
5
6
7
8
9
10
var seg = [];
seg[0] = new Segment;
var parent = seg[0];

for (var i = 1; i < SEGMENT_NUM; i++){
    seg.push(new Segment);
    seg[i].setTransform(seg[i].r * 2.5, 0);
    parent.addChild(seg[i]);
    parent = seg[i];
}

あとは CreateJS の Ticker でアニメーションをつければ完成。

1
2
3
4
5
6
function tick(){
    for (var i = 1; i < SEGMENT_NUM; i++){
        // 動きをつける
    }
    stage.update();
}

Error: Suspended

DOM Element / CreateJS

CreateJSでDOM Elementを操作します。
たぶん jQuery とかの方がイロイロできそうですが、ゲーム作る時に canvas 以外も動かしたくなった時のために。
CreateJS では DOM の操作の時も canvas いるみたいです。
対象となるHTMLは下記の感じ。

1
2
3
4
5
6
<div id="target" style="background-color: blue; width: 320px; height: 240px;">
    TEST
</div>
<canvas id="demoCanvas" width="640" height="480">
    alternate content
</canvas>

DOM Element でも今までの Shape とそんな変わりない感じに使えます。
下記は試しに 30度回転。

1
2
var content = new createjs.DOMElement('target');
content.rotation = 30;

Shape で描画する時との違いは、DOM Element は canvas からはみ出ることですかね。

Drag & Drop / CreateJS

チュートリアルの Drag & Drop やってみる。
まずはオブジェクトをクリックした時のイベント指定。

1
object.onPress = handlePress;

この時 handlePress 内で onMouseMove と onMouseUp のイベント指定しちゃいます。
そうする事でマウスのボタンが押されている間だけこの指定が有効になります。

1
2
3
4
function handlePress(){
    e.onMouseMove = handleMouseMove;
    e.onMouseUp = handleMouseUp;
}

ドラッグ時の移動はマウス座標を使って指定します。
この時対象のオブジェクトは下記のように target として取得できます。

1
2
3
function handreMouseMove(e){
    e.target.setTransform(e.stageX, e.stageY);
}

CreateJS使うと簡単ね。

氷菓のエンディングっぽい動き / CreateJS

CreateJSつかって氷菓のエンディングにあるテキストでキャラクタが描かれる動きをつくってみるよ。
2クール目EDの1分過ぎくらいのとこね。

とりあえず、前回やったお絵かきを使おうかな。
描画の時に使った座標をひと通り保存して、それをテキストに置き換えることに。
なんか既に重くなりそうな雰囲気だが…

テキストはあらかじめ a-z, A-Z で用意しとく。
CreateJSのテキストオブジェクトで1文字ずつ用意してみる。

1
2
3
4
5
6
7
8
9
var text = [];
for (var i = 65; i <= 90; i++){
    var t = new createjs.Text(String.fromCharCode(i), '20px serif', color);
    text.push(t);
}
for (var i = 97; i <= 122; i++){
    var t = new createjs.Text(String.fromCharCode(i), '20px serif', color);
    text.push(t);
}

絵を描いて再生ボタンを押すとアニメーションを再生。
その際に各座標ごとにランダムでテキストオブジェクトを割り当てます。
既にあるテキストオブジェクトから下記のようにして生成できます。

1
2
var index = Math.floor(Math.random() * text.length);
var shape = new createjs.Shape(text[index]);

正確な再現はあきらめて、ランダムな位置から描画した座標に移動、そして消えるようにしてみました。
wait で指定時間だけ動きを止めることができます。
他にも scale, rotation, alpha などランダムで動かすとよりそれらしくなると思います。

1
2
3
4
5
6
7
stage.addChild(shape).setTransform(x, y);
createjs.Tween.get(shape, { loop: true })
    .wait(800)
    .to({ x: drawX, y: drawY }, 1600)
    .wait(1000)
    .to({ x: drawX + dx, y: drawY + dy, alpha: 0 }, 1500)
    .wait(300);

こんな感じでとりあえずそれらしくは出来るのですが、ちょっと細かい絵を描くとすんごい重くなります…
まあ、座標の数が増えればそんだけテキストオブジェクトの数も増えるんだから当たり前よね。
という訳で、標準で用意されてるキャッシュの機能とかで軽くなるか試してみました。

まずは snapToPixel をオンにして描画される座標を丸め込みします。
特にビットマップ画像の時は有効みたいです。
これは親オブジェクトや stage についてもオンにしておかないと有効にならないようです。

1
2
3
4
5
// stage でオン
stage.snapToPixelEnabled = true;

// オブジェクトでオン
t.snapToPixelEnabled = true;

あとはキャッシュ機能もオンにするとだいぶ改善されます。
最初のテキストオブジェクト生成時に下記のよう指定します。
引数はキャッシュする範囲の始点と幅と高さです。
この場合はオブジェクトの大きさにします。

1
2
3
var w = t.getMeasuredWidth();
var h = t.getMeasuredHeight();
t.cache(0, 0, w, h);

拡大するときはさらにオプションで指定可能です。
移動や回転、透過してもキャッシュは有効なのでかなり自由度高いと思う。

マウスの動きに合わせて線を引く / CreateJS

チュートリアルにある Finger Paint やってみる。
マウスイベントの続きだけど、canvas (CreateJSのstage) でのイベント処理と言う感じ。

MouseMove イベントで拾ったマウス座標を使って、一つ前から今の座標まで線を引きます。
stage でも図形と同じように下記のように指定します。

1
stage.onMouseMove = handleMouseMove;

前回の座標を old として、MouseMove イベントで呼び出される度に線を引きます。
現在の座標は event.stageX, event.stageY を使います。

1
2
3
4
function handleMouseMove(event){
    line.graphics.s('#F00').ss(size, 'round').mt(old.x, old.y).lt(event.stageX, event.stageY);
    old = { x: event.stageX, y: event.stageY };
}

ちなみにキャンバスからマウスがはみ出た場合に、座標がキャンバスサイズ以上にならないオプションも用意されてます。
その際、キャンバスからはみ出た実際の座標は event.rawX, event.rawY で取得できるみたい。ホント気が利くなあ。

1
2
stage = new createjs.Stage('canvas');
stage.mouseMoveOutside = true; // はみ出ない

Mouse Interaction / CreateJS

CreateJSでつくったオブジェクトをマウスイベントに反応させる。
付属のチュートリアルに沿ってやってくよ。

クリックだけなら必要ないけど、マウスオーバー・マウスアウト使う時は下記のような宣言が必要です。
enableMouseOver は引数に一秒間に何回マウスオーバーを拾うか指定できる。(20回がデフォ)

1
2
stage = new createjs.Stage('canvas');
stage.enableMouseOver(10);

イベントはオブジェクト毎に指定します。
onClick, onMouseOver, onMouseOut, onPress, onDoubleClick などに指定可能です。

1
2
3
4
var object = new createjs.Shape();
object.onClick = function(){
    alert('クリック');
};

テキストだと隙間があるので hitArea の指定が可能です。

1
2
3
text = new createjs.Text('Text with hitArea', 'bold 20px Arial', 'red');
text.hitArea = new createjs.Shape();
text.hitArea.graphics.f('white').dr(0, 0, text.getMeasuredWidth(), text.getMeasuredHeight());

他にも、複数オブジェクトをまとめられる Container とかあって便利!
その場合は、コンテナにイベント付けたら子のオブジェクトのイベントを無視できたりと、すごくよくできてます。

1
2
container = new createjs.Container();
container.mouseChildren = false;