愛と涙のWordPress無理やりカスタマイズ事例集

1,003 views

Published on

WordCampKyoto2017

Published in: Technology

愛と涙のWordPress無理やりカスタマイズ事例集

  1. 1. 愛と涙のWordPress無理やり カスタマイズ事例集 やろうと思えば何でも出来る
  2. 2. 名古屋で株式会社ベクトルという ウェブ制作会社をしています。 設⽴して7期⽬。 ⽯川栄和 @kurudrive
  3. 3. テーマやプラグイン作ってます ウェブ制作の知識が無くても ⾼品質なホームページが作れるテンプレート
  4. 4. 今⽇伝えたい事 • WordPressはブログや企業サイトだけじゃない アプリケーションプラットフォーム。 • プラグインでもいろいろ出来るけど、 PHPの基本がわかればなんでも出来る。 どんな関数を使ってどんな処理が出来るか ヒントになれば幸いです。
  5. 5. 本⽇紹介する事例 1. 伝票管理システム 2. GPSスタンプラリー 3. ⼤企業の社内イントラ 3-1.多⾔語対応 3-2.カスタム投稿タイプを管理画⾯から制御 3-3.ユーザー権限グループを管理画⾯から制御 3-4.承認フローシステム 4. 請求書管理システム CSV出⼒
  6. 6. はじめに 途中ソースコードを紹介する場⾯があります。 限られたスペースの中で⾒やすく紹介するため、 本来必要なエスケープ処理や翻訳関数などは 省略しています。
  7. 7. 1.伝票管理システム WordPressはブログや企業サイトだけじゃない
  8. 8. 詳しい資料・ソースコード ホームページだけじゃない! 管理システムとしてのWordPress http://tech.hi-works.com/webcreative/wordpress/769
  9. 9. クライアントと業務の問題点 • 紙の伝票で処理していた → 数が多い / 営業拠点によって伝票がない • 取引先からの問い合わせもスタッフが 都度ファイルから探して確認・返答 していたので⼈的コストも多い 海外への輸出代⾏業者 クライアント 問題点
  10. 10. • ⼀つの伝票を投稿 • 各情報をカスタムフィールド に登録 • 伝票番号等で簡単検索 • 紙の伝票が不要に • レスポンシブ対応でどこから でもデータを確認 • PCに疎い依頼者には印刷し てFAX送信すればOK! 輸出コンテナの伝票情報をWordPressで管理
  11. 11. ログインユーザーで表⽰情報を切り分け 輸送依頼者が⾃分で コンテナの状況を閲覧出来る。 • クライアント毎にユーザーを発⾏ • 伝票(投稿)の「投稿者」には 該当クライアントのユーザーアカ ウントを割り当てる • クライアントが直接ログインする と、そのアカウントの 伝票(投稿)しか⾒れないように しておく
  12. 12. 伝票毎にクライアントにメール送信 • データ登録通知 • 請求通知 ContactForm7を応⽤
  13. 13. 2.GPSスタンプラリー データの保存先にカスタム投稿タイプを利⽤
  14. 14. お城めぐりGPSスタンプラリー kojoki.jp
  15. 15. お城に⾏ってスマホからチェックイン
  16. 16. ⾃分の⾏ったお城の履歴
  17. 17. 詳しい資料・ソースコード WordFes 2014 WordPressと外部APIとの連携 https://www.slideshare.net/kurudrive/wordpressapi
  18. 18. 主な処理 • 投稿にお城のデータを⼊⼒ • サービス利⽤者はWordPressのユーザーに 登録・ログインして利⽤ • GPSで位置情報の判定に成功したら、 チェックイン情報をカスタム投稿タイプに保存
  19. 19. チェックイン情報の保存 カスタム投稿タイプにデータを保存する
  20. 20. ユーザーがチェックインした情報を保存したい 最低限保存する情報 • チェックインしたユーザーは誰か? ログイン中のユーザーID • チェックインしたお城はどこか? お城ページの投稿ID
  21. 21. ログイン中のユーザーIDと チェックインしたお城のIDを取得 ログインしているユーザーID // ログインしているユーザー情報を取得 $user = wp_get_current_user(); // ユーザーID $user_id = $user->ID; クッキーなどからチェックインした城のIDを取得 If ( isset( $_COOKIE["checkin_spot_id"] ) ) { $checkin_id = $_COOKIE["checkin_spot_id"]; } チェックイン成功ページでの処理
  22. 22. チェックイン情報の保存先と保存情報 チェックイン情報 保存⽤の カスタム投稿タイプに投稿していく 最低限保存する情報 • チェックインしたユーザーIDの保存先 投稿者に設定 • チェックインしたお城ID(投稿ID)の保存先 タイトル⼜はカスタムフィールドに保存
  23. 23. 投稿を⾃動で追加する $checkin_post = array(); // 投稿タイプ $checkin_post['post_type'] = 'checkin'; // 投稿の状態 $checkin_post['post_status'] = 'publish'; // 投稿のタイトル $checkin_post['post_title'] = get_the_title($checkin_id); // 投稿の作成者 $checkin_post['post_author'] = $user_id; タイトルはチェックインした城の名前 $posted_id = wp_insert_post( $checkin_post ); 新規投稿する関数 新規投稿する情報 投稿に成功した時に、成功した投稿のIDが返ってくる
  24. 24. カスタムフィールドにお城のIDを保存 add_post_meta($posted_id, 'checkin_spot_id', $checkin_id); 上記で成功したチェックイン情報の投稿ID 保存先のカスタムフィールドのキー チェックインしたお城ID
  25. 25. 保存結果
  26. 26. 保存したチェックインデータの引き出し例 $posts = get_posts(array( 'post_type' => 'checkin', 'posts_per_page' => -1, 'author' => $user_id, )); ログイン中のユーザーのチェックイン履歴 投稿タイプがʼcheckinʼ 作成者が $user_id ログイン中のユーザーの投稿
  27. 27. 結果出⼒応⽤例
  28. 28. その他の 投稿・削除⽤関数 • 既存投稿の更新 wp_update_post(); • 既存投稿の削除 wp_delete_post(); • カスタムフィールドの値の更新 update_post_meta(); • カスタムフィールドの値の削除 delete_post_meta();
  29. 29. 3.社内イントラ 多⾔語多機能サイト
  30. 30. 最初の主な要望 • 多⾔語対応(英語 / ⽇本語) • 編集権限制御 部署毎の担当者が⾃部署のコンテンツだけ変更可能に 部署毎のコンテンツ : 固定ページと部署毎のニュース • MicrosoftのActive Directoryとユーザーアカウントを連携 部署⽤のWordPressユーザーを発⾏して〜という運⽤ではない • 閲覧制御 • イベントカレンダー • 電話帳機能(CSVインポート・エクスポート) • 他多数
  31. 31. 基本構成検討 → マルチサイトは多⾔語対応の為に使った (終わりのはじまり...) 要望 : ⾃分の部署コンテンツだけ編集出来る出来るように → マルチサイトにして⾃分のサイトだけ編集させれば... だがしかし・・・ • 将来的に⾔語毎に違う情報配置要望の可能性 • 部署によっては親部署・⼦部署がある (必ず並列じゃない)
  32. 32. 3-1.多⾔語対応
  33. 33. 当初の多⾔語対応想定 • マルチサイトで1⾔語1サイト • ⼀つの記事毎に他の⾔語(⼦サイト)で どの記事に該当するのかを指定して紐付ける Multi language switcher とは プラグイン「Multi language switcher」の利⽤を想定。 (と⾔うかそれが前提条件で初期構築…)
  34. 34. Multi language switcher の⻑所と短所 • ⾔語毎のサイトの編集画⾯にログインして記事を書いて紐付け • ⽇本語の翻訳が存在しないページでは英語を表⽰すれば良いという 場合でも⼿動で英語コンテンツを登録する必要がある。 ⻑所 • シンプル(⼦サイトの記事同⼠を紐づけているだけ) • このプラグインを停⽌しても⼤きな不具合は発⽣しない (⾔語毎に別々の⼦サイトになるだけ) • サイト(⾔語)毎に全然違うデザインやレイアウトにしやすい。 短所
  35. 35. 先⽅で関係各位が集まる中、多⾔語の表⽰・編集を実演 ( ゚д゚) こんな感じっす!
  36. 36. ちょっとこれは⾯倒くさいなぁ... ⼀つの画⾯で出来ないの?
  37. 37. / ̄ ̄\ /ノ( _ノ \ | ⌒(( ●)(●) .| (__人__) /⌒l | ` ⌒´ノ |`'''| / ⌒ヽ } | | __________て / へ \ }__/ / | | ̄ ̄ ̄ ̄ ̄ ̄ ̄| | |( / / | ノ ノ | | \ / | | |’, ・ ( _ ノ | \´ _ | | \ ノ( / | | | , ’ | \_,, -‐ ''"  ̄ ゙̄''―---└'´ ̄`ヽ | | | て .| __ ノ _| | | ( ヽ _,, -‐ ''" ̄|_ ̄_o o o___|_|r'" ( ディレクションに関するセッションではないので詳細は割愛 )
  38. 38. Multi language switcher を諦め独⾃実装 1画⾯で英語と⽇本語を⼊⼒する独⾃カスタマイズ...。 • 副⾔語(⽇本語)のタイトルと本⽂欄をカスタムフィールドで作成 • ⽇本語表⽰の場合はカスタムフィールドに保存されている情報を表⽰ • ⾔語の判別はURLにパラメーターをつける事で実施する ( 検索システム対策 ) 基本的な構造案
  39. 39. 主な処理 1.サイト内のリンクURLを、⾔語判別⽤ パラメーター付きのURLに置換する(検索対策) 2.⾔語パラメータを取得して英語か⽇本語か判別 3.⽇本語表⽰の場合はタイトルと本⽂を カスタムフィールドの内容に差し替えて表⽰する
  40. 40. URLに⾔語判定⽤パラメーターをつける サイト内のリンク先URLを差し替えないといけない。 例) 通常(英語)http://○○○.com/abc ⽇本語 http://○○○.com/abc?lang=ja
  41. 41. function change_url( $permalink ){ // 差し替えたいURL $permalink = 'https://www.google.co.jp'; // 戻す return $permalink; } add_filter( 'the_permalink', 'change_url' ); the_permalink() タグを使ってある所のリンク先が 全部 google.co.jp になります。 フィルターフックの例 add_filter( 'the_permalink', 差し替える関数名 );
  42. 42. function lang_switch_link_url( $permalink ){ if ( !is_admin() ){ // 現在の⾔語を取得 $locale = get_locale(); // URLに⾔語パラメーターを追加 $permalink = add_query_arg( 'lang', $locale, $permalink ); } return $permalink; } add_filter( 'the_permalink', 'lang_switch_link_url' ,10,2 ); ※補⾜ • get_locale() で取得する現在の表⽰⾔語の値は別の箇所でフィルターで書き換え済みの想定 • 実際には外部リンクへの差し替えが⾏われている場合の処理、 及び the_permalink 以外で出⼒されるURLの書き換え処理なども必要になってきます。 フィルターでURLを置換 元のURL パラメーター名 パラメーターの値
  43. 43. タイトルや本⽂を差し替える function lang_switch_title( $title ){ if ( get_locale() == "ja" && !is_admin() ){ // ⽇本語表⽰で管理画⾯以外の場合に差し替え $title = get_post_meta( $post->ID, 'lang_title_ja', true ); } return $title; } add_filter( 'the_title', 'lang_switch_title', 10, 2 ); ⽇本語のタイトルが保存された カスタムフィールド
  44. 44. 3-2.部署毎のニュースコンテンツ
  45. 45. 要望 → 部署毎のカスタム投稿タイプで実装 部署(のカスタム投稿タイプ)は定期的に 再編が予想される ⾃部署以外の記事は編集できないように 懸念事項
  46. 46. 管理画⾯からニュース⽤投稿タイプを管理 • 各部署のニュース⽤カスタム投稿タイプを 管理画⾯から追加・削除出来るように • 通常独⾃の設定項⽬を保存する場合 専⽤のオプション項⽬設定画⾯を1ページ作って そこに設定項⽬のフォームを設置する → フォームを作るのも⾯倒 → ⼊⼒項⽬が増減するので余計⼿間がかかりそう ニュース⽤のカスタム投稿タイプ⾃体を 『NEWS設定』というカスタム投稿タイプで制御する
  47. 47. 管理画⾯での表⽰例
  48. 48. NEWS設定の概要 1. カスタム投稿タイプ「NEWS設定」に、どんな名前の NEWS⽤カスタム投稿タイプを作るか 「タイトル/スラッグ」を⼊⼒して投稿する 2. カスタム投稿タイプ「NEWS設定」に投稿された投稿を get_posts()などで取得してループする 3. ループの中で カスタム投稿タイプを作成する register_post_type() を書くと、NEWS設定に投稿した 分だけ新しくニュース⽤の投稿タイプが作成される。
  49. 49. NEWS設定を取得 // NEWS設定に投稿されている情報を取得する関数 function news_manage_news_settings(){ $args = array( 'posts_per_page' => -1, 'post_type' => 'news_setting', ); $news_settings = get_posts($args); return $news_settings; } 予めカスタム投稿タイプ『NEWS設定( post_type = news_setting )』を 作成して複数投稿されている前提
  50. 50. NEWS⽤投稿タイプを⽣成 function news_manage_add_post_type_news() { // NEWS設定に投稿されている投稿データを取得 $news_settings = news_settings(); // NEWS設定の情報をループしてカスタム投稿タイプを作る foreach ($news_settings as $key => $news_setting) { $labels = array( 'name' => $news_setting->post_title, 'singular_name' => $news_setting->post_title, 'menu_name' => 'NEWS [ '.$news_setting->post_title.' ]', ); $args = array( 'labels' => $labels, // 中略 ); register_post_type( $news_setting->post_name.'_news', $args ); } } add_action( 'init', 'news_manage_add_post_type_news' );
  51. 51. ポイント • プラグイン Contact Form 7 • プラグイン Smart Custom Fields • プラグイン VK All in one Expansion Unit の カスタム投稿タイプマネージャー カスタム投稿タイプを実際に表⽰するページ 情報ではなく、管理⽤の項⽬として使う
  52. 52. 3-3.編集権限の設定
  53. 53. NEWSを編集出来る権限の設定 NEWS設定で作成されたカスタム投稿タイプ → 特定の部署のメンバーしか投稿できないように 記事を投稿・編集・削除出来る権限を設定しておく $args = array( // 中略 'capability_type' => array( $news_setting->post_name.'_news', $news_setting->post_name.'_newss' ), 'map_meta_cap' => true, //中略 ); register_post_type( $news_setting->post_name.'_news', $args );
  54. 54. ユーザー権限の管理をどうするか? プラグイン User Role editor ? • 細かく設定できすぎる → 設定が⾯等 → クライアントが使いこなせない • 要望がイレギュラーすぎて既存のプラグインでは 存在すると思えない ⾃作しましょう!
  55. 55. ユーザー権限グループ管理機能
  56. 56. ユーザー権限管理⽤カスタム投稿タイプ追加 カスタム投稿タイプ「ユーザー権限グループ」を作成する NEWS設定 で投稿されている情報
  57. 57. チェックボックスの⽣成 // NEWS設定に投稿されている情報を取得する関数 $news_settings = news_settings(); // NEWS投稿タイプをループしてチェックボックスを⽣成 echo '<ul>'; foreach ($news_settings as $key => $value) { $news_post_type = $value->post_name; $news_label = $value->post_title; // チェックボックス出⼒ echo '<li><label><input type="checkbox" name="edit_post_type_items[]" value="'.$news_post_type.ʼ”>'. $value->post_title.'</label></li>'; } echo '</ul>';
  58. 58. 参考 /*-------------------------------------------*/ /* 権限グループ編集画⾯ _ 投稿タイプ別の⼊⼒フィールドの⽣成 /*-------------------------------------------*/ add_action('user_group_setting_meta_box', 'eam_user_edit_post_type_items',9); function eam_user_edit_post_type_items(){ global $post; wp_nonce_field( wp_create_nonce(__FILE__), 'noncename__edit_post_type_items' ); // 現在保存されている編集可能投稿タイプの値を取得 $edit_post_type_items = get_post_meta( $post->ID, 'edit_post_type_items', true ); // NEWS投稿タイプの情報を取得 $args = array( ʻpost_typeʼ => ʻnews_settingʼ, // NEWS設定の投稿タイプ ʻposts_per_pageʼ => -1, // 投稿を全件取得 ); $news_settings = get_posts($args); // NEWS投稿タイプをループしてチェックボックスを⽣成 if ( $news_settings ){ echo '<ul>'; foreach ($news_settings as $key => $value) { $news_post_type = esc_attr( $value->post_name ); $news_label = esc_html( $value->post_title ); // 既にチェックが保存されているものはチェックをつける $checked = ''; if ( $edit_post_type_items && in_array($value->post_name, $edit_post_type_items) ) { $checked = " checked"; } // チェックボックス出⼒ echo '<li><label><input type="checkbox" name="edit_post_type_items[]" value="'.$news_post_type.'"'.$checked.'>'.$news_label.'</label></li>'; } // foreach ($news_settings as $key => $value) { echo '</ul>'; } // if ( $news_settings ){ }
  59. 59. ここまでの状況 • カスタム投稿タイプ「ユーザー権限グループ」に 権限グループ情報が投稿されている • 各権限グループには 編集可能なニュース⽤カスタム投稿タイプの情報が カスタムフィールドに配列で保存されている
  60. 60. 実際にユーザー権限を作成 WordPressのユーザー権限を発⾏する
  61. 61. 権限を作成・更新するタイミング カスタムフィールドを保存処理時 権限作成・更新⽤の関数を作成して実⾏させる。
  62. 62. ユーザー権限グループのロールを発⾏ function eam_update_role( $post_id ){ // 変更のあったユーザー権限グループの投稿 $post = get_post( $post_id ); $role_slug = $post->post_name; $role_label = $post->post_title; // 変更のあったロールだけ⼀旦削除 remove_role( $role_slug ); // ロールを再度作成 add_role( $role_slug, $role_label, array('read'=> true) ); }
  63. 63. ユーザーの権限グループに追加される
  64. 64. ユーザー権限グループに権限を設定 // 作成した各権限グループのロールを取得 $role = get_role( $role_slug ); // グループが編集可能に設定されている情報を取得 $edit_post_types = get_post_meta(・・・); foreach ($edit_post_types as $key => $edit_post_type ) { // チェックされている投稿タイプの権限を付与する $role->add_cap( 'add_'.$edit_post_type' ); $role->add_cap( ʼedit_'.$edit_post_type' ); $role->add_cap( ʼdelete_'.$edit_post_type' ); (中略) }
  65. 65. 特定部署のユーザーでのログインイメージ
  66. 66. 先⽅で編集権限の動作確認 ____ / \ /\ キリッ . / (ー) (ー)\ / ⌒(__人__)⌒ \ できたお | |r┬-| | \ `ー'´ / ノ \ /´ ヽ
  67. 67. 確認・承認はどうやるの?
  68. 68. / ̄ ̄ ̄\ / ─ ─ \ / <○> <○> \. | (__人__) | \ ` ⌒´ / / \ は?
  69. 69. / ̄ ̄\ /ノ( _ノ \ | ⌒(( ●)(●) .| (__人__) /⌒l | ` ⌒´ノ |`'''| / ⌒ヽ } | | __________て / へ \ }__/ / | | ̄ ̄ ̄ ̄ ̄ ̄ ̄| | |( / / | ノ ノ | | \ / | | |’, ・ ( _ ノ | \´ _ | | \ ノ( / | | | , ’ | \_,, -‐ ''"  ̄ ゙̄''―---└'´ ̄`ヽ | | | て .| __ ノ _| | | ( ヽ _,, -‐ ''" ̄|_ ̄_o o o___|_|r'" ( ディレクションに関するセッションではないので詳細は割愛 )
  70. 70. 3-4.承認フロー実装します!
  71. 71. この部分のソースについて 処理が複雑でスライドでは紹介できないので、 スライドでは概念の説明のみとなります。 参考ソースコードを確認したい場合は 以下のURLにアップしてあります。 https://github.com/kurudrive/wc-kyoto-2017
  72. 72. 承認フローの機能 • 公開権限を持つ/持たないユーザーグループ • 下位権限のユーザーは新規投稿時に承認依頼をする (公開権限を持たない) • 下位権限のユーザーから承認依頼があって、 公開権限を持つユーザーが公開処理を⾏うと公開 • 下位権限ユーザーは公開中の記事も直接編集不可
  73. 73. 公開権限の有無のグループ分け 公開権限の有無の設定
  74. 74. 公開権限の有無の処理 // 作成した各権限グループのロールを取得 $role = get_role( $role_slug ); // 公開可能かどうかの情報を取得 $publish_role = get_post_meta(・・・); // 公開権限があるユーザー if ( $publish_role ){ $role->add_cap( 'publish_'.$edit_post_type.'s' ); $role->add_cap( 'publish_others_'.$edit_post_type.'s' ); $role->add_cap( 'edit_published_'.$edit_post_type.'s' ); $role->add_cap( 'delete_others_'.$edit_post_type.'s' ); $role- >add_cap( 'delete_published_'.$edit_post_type.'s' ); }
  75. 75. 下位権限のユーザーは承認依頼をさせる 1. この位置に 2. 公開権限を持つ ユーザーのプルダウン 3. 承認依頼ボタン // この位置に処理を追加 add_action( 'post_submitbox_start', '◯◯'); // 権限グループを取得 $roles = wp_roles()->roles;
  76. 76. 承認依頼メールの送信 // 承認依頼ボタン <input type="submit" name="send_review_mail" value="承認依頼"> function edit_flow__send_review_mail(){ // 承認依頼のボタンが押されたら if ( isset($_POST['send_review_mail']) ){ // メール送信処理を書く } } add_action('save_post', 'edit_flow__send_review_mail');
  77. 77. レビュー依頼メールの処理と内容 wp_mail() → メールを送信する事ができる メールの本⽂に確認するURLを貼り付けておく admin_url().'post.php?post='.$_POST['ID'].'&action=edit'
  78. 78. 承認依頼のあった記事の公開・⾮承認 公開 ⾮承認のメール送信
  79. 79. 公開済の記事の編集(複製1) 「複製して編集」のリンクを追加 $links = admin_url().'post-new.php?post_type='.$post_type; $links .= '&master_id='.$post->ID; コピー元の投稿ID
  80. 80. 公開済の記事の編集(複製2) /*-------------------------------------------*/ /* 新規複製 _ 複製を実⾏ /*-------------------------------------------*/ function edit_flow__create_copy(){ // 管理画⾯のURLに複製識別⽤のURLが含まれていたら if ( isset($_GET['master_id']) ){ $master_id = esc_html($_GET['master_id']); // 記事の複製を実⾏ $copy_post_id = edit_flow__copy_post( $master_id ); // 複製した記事の編集画⾯へリダイレクト $url = admin_url().'post.php?post='.$copy_post_id.'&action=edit'; header("Location: {$url}"); } } add_action('admin_init', 'edit_flow__create_copy');
  81. 81. 公開済の記事の編集(複製3) wp_insert_post() / add_post_meta() を使って複製 POINT 複製元の記事(現在公開中の記事)IDを カスタムフィールドに保存しておく。
  82. 82. 公開承認処理 更新記事 公開記事 更新内容で上書き 更新記事を削除
  83. 83. 4.請求書・⾒積書管理システム
  84. 84. 主な機能 • 投稿に請求データを登録して請求書を発⾏ • 同様にカスタム投稿タイプで⾒積書も発⾏ • ⾒積書を wp_insert_post() で複製したり、 請求書のカスタム投稿タイプに複製 • クラウド会計ソフト⽤にCSV出⼒
  85. 85. CSVへの出⼒
  86. 86. 実際のソースコード https://github.com/vektor-inc/bill-vektor/blob/master/inc/export/class.csv-export.php https://goo.gl/H3yKJc CSV出⼒部分のソースは下記にあります
  87. 87. CSVダウンロードボタンに送信するvalueを指定 <button type="submit" name="action" value="csv_mf"> CSVエクスポート </button>
  88. 88. CSVダウンロードを受け取ったらCSVを発⾏ function export_csv(){ // CSVダウンロードを押されたかどうか if ( $_GET['action'] == 'csv_mf' ){ // 変数 $csv に 出⼒するCSVを成形 // httpヘッダーを記載 } // CSVを出⼒ echo $csv; // 終了 die(); } add_action( 'init', export_csv );
  89. 89. httpヘッダーにcsvである事と⽂字コードを記載 header("Content-Type: text/csv; charset=utf-8"); utf-8の場合 header("Content-Type: text/csv; charset=shift_jis"); $full_csv = mb_convert_encoding( $full_csv, "SJIS" ); shift-jisの場合(エクセルなどで開く場合)
  90. 90. まとめ カスタム投稿タイプ / カスタムフィールド wp_insert_post() / add_post_meta() wp_mail() 使い⽅次第でWordPressはなんでも出来る!
  91. 91. けれども
  92. 92. 仕様はしっかり固めよう
  93. 93. Thank you.

×