Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

5,833 views

Published on

WordCampKyoto2017

Published in: Technology
  • Be the first to comment

愛と涙の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.

×