Changeset 3418932
- Timestamp:
- 12/13/2025 01:59:29 PM (4 months ago)
- Location:
- blogcard-for-wp
- Files:
-
- 16 added
- 23 edited
- 1 copied
-
assets/screenshot-1.png (modified) (1 prop) (previous)
-
assets/screenshot-2.png (modified) (1 prop) (previous)
-
assets/screenshot-3.png (modified) (1 prop) (previous)
-
assets/screenshot-4.png (modified) (1 prop) (previous)
-
assets/screenshot-5.png (modified) (1 prop) (previous)
-
tags/2.0.6 (copied) (copied from blogcard-for-wp/trunk)
-
tags/2.0.6/.prettierignore (added)
-
tags/2.0.6/.wordpress-org (added)
-
tags/2.0.6/.wordpress-org/icon.webp (added)
-
tags/2.0.6/.wordpress-org/screenshot-1.png (added)
-
tags/2.0.6/.wordpress-org/screenshot-2.png (added)
-
tags/2.0.6/.wordpress-org/screenshot-3.png (added)
-
tags/2.0.6/.wordpress-org/screenshot-4.png (added)
-
tags/2.0.6/.wordpress-org/screenshot-5.png (added)
-
tags/2.0.6/blogcard-for-wp.php (modified) (13 diffs)
-
tags/2.0.6/build/block.json (modified) (1 diff)
-
tags/2.0.6/build/index-rtl.css (modified) (1 diff)
-
tags/2.0.6/build/index.asset.php (modified) (1 diff)
-
tags/2.0.6/build/index.css (modified) (1 diff)
-
tags/2.0.6/build/index.js (modified) (1 diff)
-
tags/2.0.6/build/style-index-rtl.css (modified) (1 diff)
-
tags/2.0.6/build/style-index.css (modified) (1 diff)
-
tags/2.0.6/readme.txt (modified) (2 diffs)
-
trunk/.prettierignore (added)
-
trunk/.wordpress-org (added)
-
trunk/.wordpress-org/icon.webp (added)
-
trunk/.wordpress-org/screenshot-1.png (added)
-
trunk/.wordpress-org/screenshot-2.png (added)
-
trunk/.wordpress-org/screenshot-3.png (added)
-
trunk/.wordpress-org/screenshot-4.png (added)
-
trunk/.wordpress-org/screenshot-5.png (added)
-
trunk/blogcard-for-wp.php (modified) (13 diffs)
-
trunk/build/block.json (modified) (1 diff)
-
trunk/build/index-rtl.css (modified) (1 diff)
-
trunk/build/index.asset.php (modified) (1 diff)
-
trunk/build/index.css (modified) (1 diff)
-
trunk/build/index.js (modified) (1 diff)
-
trunk/build/style-index-rtl.css (modified) (1 diff)
-
trunk/build/style-index.css (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
blogcard-for-wp/assets/screenshot-1.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
blogcard-for-wp/assets/screenshot-2.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
blogcard-for-wp/assets/screenshot-3.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
blogcard-for-wp/assets/screenshot-4.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
blogcard-for-wp/assets/screenshot-5.png
-
Property
svn:mime-type
changed from
application/octet-streamtoimage/png
-
Property
svn:mime-type
changed from
-
blogcard-for-wp/tags/2.0.6/blogcard-for-wp.php
r3368864 r3418932 2 2 3 3 /** 4 * Plugin Name: Blogcard for WP4 * Plugin Name: SU Blocks - Blogcard 5 5 * Plugin URI: https://wordpress.org/plugins/blogcard-for-wp/ 6 6 * Description: URLを入力してブログカードを生成するブロックプラグイン 7 * Version: 2.0. 27 * Version: 2.0.6 8 8 * Author: Takashi Fujisaki 9 9 * License: GPL v2 or later … … 12 12 13 13 // 直接アクセスを防ぐ 14 if ( !defined('ABSPATH')) {15 exit;14 if ( ! defined( 'ABSPATH' ) ) { 15 exit; 16 16 } 17 17 18 18 // プラグインの定数 19 define('WPBC_PLUGIN_PATH', plugin_dir_path(__FILE__)); 19 define( 'WPBC_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); 20 21 /** 22 * カスタムブロックカテゴリーを追加 23 */ 24 function wpbc_block_categories( $categories, $editor_context ) { 25 if ( ! empty( $editor_context->post ) ) { 26 // 既存のカテゴリーが存在するかチェック 27 $exists = wp_list_pluck( $categories, 'slug' ); 28 if ( ! in_array( 'su-blocks', $exists, true ) ) { 29 array_push( 30 $categories, 31 array( 32 'slug' => 'su-blocks', 33 'title' => 'SU Blocks', 34 ) 35 ); 36 } 37 } 38 return $categories; 39 } 40 add_filter( 'block_categories_all', 'wpbc_block_categories', 10, 2 ); 20 41 21 42 /** … … 23 44 */ 24 45 function wpbc_init() { 25 // テキストドメインの読み込み26 load_plugin_textdomain('wpbc', false, dirname(plugin_basename(__FILE__)) . '/languages');27 28 // ブロックの登録29 register_block_type(WPBC_PLUGIN_PATH . 'build/block.json');30 } 31 add_action( 'init', 'wpbc_init');46 // テキストドメインの読み込み 47 load_plugin_textdomain( 'wpbc', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); 48 49 // ブロックの登録 50 register_block_type( WPBC_PLUGIN_PATH . 'build/block.json' ); 51 } 52 add_action( 'init', 'wpbc_init' ); 32 53 33 54 /** … … 35 56 */ 36 57 function wpbc_rest_api_init() { 37 register_rest_route('wpbc/v1', '/metadata', [ 38 'methods' => 'POST', 39 'callback' => 'wpbc_get_metadata', 40 'permission_callback' => function () { 41 return current_user_can('edit_posts'); 42 }, 43 'args' => [ 44 'url' => [ 45 'required' => true, 46 'type' => 'string', 47 'sanitize_callback' => 'esc_url_raw', 48 ], 49 ], 50 ]); 51 52 register_rest_route('wpbc/v1', '/internal-metadata', [ 53 'methods' => 'GET', 54 'callback' => 'wpbc_get_internal_metadata', 55 'permission_callback' => function () { 56 return current_user_can('edit_posts'); 57 }, 58 'args' => [ 59 'url' => [ 60 'required' => true, 61 'type' => 'string', 62 'sanitize_callback' => 'esc_url_raw', 63 ], 64 ], 65 ]); 66 67 register_rest_route('wpbc/v1', '/search', [ 68 'methods' => 'GET', 69 'callback' => 'wpbc_search_posts', 70 'permission_callback' => function () { 71 return current_user_can('edit_posts'); 72 }, 73 'args' => [ 74 'q' => [ 75 'required' => true, 76 'type' => 'string', 77 'sanitize_callback' => 'sanitize_text_field', 78 ], 79 ], 80 ]); 81 82 register_rest_route('wpbc/v1', '/clear-cache', [ 83 'methods' => 'POST', 84 'callback' => 'wpbc_clear_cache', 85 'permission_callback' => function () { 86 return current_user_can('edit_posts'); 87 }, 88 'args' => [ 89 'type' => [ 90 'type' => 'string', 91 'default' => 'plugin', 92 'enum' => ['plugin', 'all'] 93 ], 94 ], 95 ]); 96 } 97 add_action('rest_api_init', 'wpbc_rest_api_init'); 58 register_rest_route( 59 'wpbc/v1', 60 '/metadata', 61 array( 62 'methods' => 'POST', 63 'callback' => 'wpbc_get_metadata', 64 'permission_callback' => function () { 65 return current_user_can( 'edit_posts' ); 66 }, 67 'args' => array( 68 'url' => array( 69 'required' => true, 70 'type' => 'string', 71 'sanitize_callback' => 'esc_url_raw', 72 ), 73 ), 74 ) 75 ); 76 77 register_rest_route( 78 'wpbc/v1', 79 '/internal-metadata', 80 array( 81 'methods' => 'GET', 82 'callback' => 'wpbc_get_internal_metadata', 83 'permission_callback' => function () { 84 return current_user_can( 'edit_posts' ); 85 }, 86 'args' => array( 87 'url' => array( 88 'required' => true, 89 'type' => 'string', 90 'sanitize_callback' => 'esc_url_raw', 91 ), 92 ), 93 ) 94 ); 95 96 register_rest_route( 97 'wpbc/v1', 98 '/search', 99 array( 100 'methods' => 'GET', 101 'callback' => 'wpbc_search_posts', 102 'permission_callback' => function () { 103 return current_user_can( 'edit_posts' ); 104 }, 105 'args' => array( 106 'q' => array( 107 'required' => true, 108 'type' => 'string', 109 'sanitize_callback' => 'sanitize_text_field', 110 ), 111 ), 112 ) 113 ); 114 115 register_rest_route( 116 'wpbc/v1', 117 '/clear-cache', 118 array( 119 'methods' => 'POST', 120 'callback' => 'wpbc_clear_cache', 121 'permission_callback' => function () { 122 return current_user_can( 'edit_posts' ); 123 }, 124 'args' => array( 125 'type' => array( 126 'type' => 'string', 127 'default' => 'plugin', 128 'enum' => array( 'plugin', 'all' ), 129 ), 130 ), 131 ) 132 ); 133 } 134 add_action( 'rest_api_init', 'wpbc_rest_api_init' ); 98 135 99 136 /** 100 137 * メタデータ取得のREST APIコールバック 101 138 */ 102 function wpbc_get_metadata( $request) {103 $url = $request->get_param('url');104 105 if (empty($url)) {106 return new WP_Error('missing_url', 'URLが指定されていません。', ['status' => 400]);107 }108 109 $metadata = wpbc_fetch_metadata($url);110 111 // WP_Errorオブジェクトの場合はそのまま返す112 if (is_wp_error($metadata)) {113 return $metadata;114 }115 116 // falseの場合は一般的なエラー117 if ($metadata === false) {118 return new WP_Error('metadata_fetch_failed', 'メタデータの取得に失敗しました。', ['status' => 500]);119 }120 121 return [ 122 'success' => true,123 'data' => $metadata 124 ];139 function wpbc_get_metadata( $request ) { 140 $url = $request->get_param( 'url' ); 141 142 if ( empty( $url ) ) { 143 return new WP_Error( 'missing_url', 'URLが指定されていません。', array( 'status' => 400 ) ); 144 } 145 146 $metadata = wpbc_fetch_metadata( $url ); 147 148 // WP_Errorオブジェクトの場合はそのまま返す 149 if ( is_wp_error( $metadata ) ) { 150 return $metadata; 151 } 152 153 // falseの場合は一般的なエラー 154 if ( false === $metadata ) { 155 return new WP_Error( 'metadata_fetch_failed', 'メタデータの取得に失敗しました。', array( 'status' => 500 ) ); 156 } 157 158 return array( 159 'success' => true, 160 'data' => $metadata, 161 ); 125 162 } 126 163 … … 128 165 * 内部メタデータ取得のREST APIコールバック 129 166 */ 130 function wpbc_get_internal_metadata( $request) {131 $url = $request->get_param('url');132 133 if (empty($url)) {134 return new WP_Error('missing_url', 'URLが指定されていません。', ['status' => 400]);135 }136 137 $metadata = wpbc_fetch_internal_metadata($url);138 139 if (is_wp_error($metadata)) {140 return $metadata;141 }142 143 return [ 144 'success' => true,145 'data' => $metadata 146 ];167 function wpbc_get_internal_metadata( $request ) { 168 $url = $request->get_param( 'url' ); 169 170 if ( empty( $url ) ) { 171 return new WP_Error( 'missing_url', 'URLが指定されていません。', array( 'status' => 400 ) ); 172 } 173 174 $metadata = wpbc_fetch_internal_metadata( $url ); 175 176 if ( is_wp_error( $metadata ) ) { 177 return $metadata; 178 } 179 180 return array( 181 'success' => true, 182 'data' => $metadata, 183 ); 147 184 } 148 185 … … 150 187 * URLが内部サイトのURLかどうかを判定する 151 188 */ 152 function wpbc_is_internal_url( $url) {153 if (empty($url)) {154 return false;155 }156 157 $site_url= get_site_url();158 $parsed_site_url = parse_url($site_url);159 $parsed_url = parse_url($url);160 161 if (!$parsed_site_url || !$parsed_url) {162 return false;163 }164 165 $site_host = $parsed_site_url['host'];166 $url_host= $parsed_url['host'];167 168 // 完全一致169 if ($site_host === $url_host) {170 return true;171 }172 173 // サブドメインのチェック174 // 例:www.example.com は example.com のサブドメイン175 $site_suffix = '.' . $url_host;176 $url_suffix= '.' . $site_host;177 if (178 substr($site_host, -strlen($site_suffix)) === $site_suffix ||179 substr($url_host, -strlen($url_suffix)) === $url_suffix180 ) {181 return true;182 }183 184 return false;189 function wpbc_is_internal_url( $url ) { 190 if ( empty( $url ) ) { 191 return false; 192 } 193 194 $site_url = get_site_url(); 195 $parsed_site_url = parse_url( $site_url ); 196 $parsed_url = parse_url( $url ); 197 198 if ( ! $parsed_site_url || ! $parsed_url ) { 199 return false; 200 } 201 202 $site_host = $parsed_site_url['host']; 203 $url_host = $parsed_url['host']; 204 205 // 完全一致 206 if ( $site_host === $url_host ) { 207 return true; 208 } 209 210 // サブドメインのチェック 211 // 例:www.example.com は example.com のサブドメイン 212 $site_suffix = '.' . $url_host; 213 $url_suffix = '.' . $site_host; 214 if ( 215 substr( $site_host, -strlen( $site_suffix ) ) === $site_suffix || 216 substr( $url_host, -strlen( $url_suffix ) ) === $url_suffix 217 ) { 218 return true; 219 } 220 221 return false; 185 222 } 186 223 … … 188 225 * メタデータを取得する関数 189 226 */ 190 function wpbc_fetch_metadata($url) { 191 // キャッシュキーを生成 192 $cache_key = 'wpbc_metadata_' . md5($url); 193 194 // キャッシュから取得を試行 195 $cached_data = get_transient($cache_key); 196 if ($cached_data !== false) { 197 // キャッシュから取得した場合はcachedフラグを追加 198 $cached_data['cached'] = true; 199 return $cached_data; 200 } 201 202 // URLの検証 203 if (!filter_var($url, FILTER_VALIDATE_URL)) { 204 return new WP_Error('invalid_url', '無効なURL形式です。', ['status' => 400]); 205 } 206 207 // 内部URLの場合は内部メタデータ取得関数を使用 208 if (wpbc_is_internal_url($url)) { 209 $metadata = wpbc_fetch_internal_metadata($url); 210 if (is_wp_error($metadata)) { 211 return $metadata; 212 } 213 214 // キャッシュに保存(24時間) 215 set_transient($cache_key, $metadata, 24 * HOUR_IN_SECONDS); 216 217 return $metadata; 218 } 219 220 $metadata = [ 221 'title' => '', 222 'description' => '', 223 'thumbnail' => '', 224 'domain' => parse_url($url, PHP_URL_HOST), 225 'final_url' => $url // リダイレクト後の最終URL 226 ]; 227 228 // HTTPリクエストでメタデータを取得(リダイレクトを追跡) 229 $response = wp_remote_get($url, [ 230 'timeout' => 20, // タイムアウトを20秒に延長 231 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 232 'redirection' => 5, // 最大5回までリダイレクトを追跡 233 'sslverify' => false, // SSL証明書の検証を無効化(テスト用) 234 'headers' => [ 235 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 236 'Accept-Language' => 'ja-JP,ja;q=0.9,en;q=0.8', 237 'Accept-Encoding' => 'gzip, deflate, br', 238 'Connection' => 'keep-alive', 239 'Upgrade-Insecure-Requests' => '1', 240 'Sec-Fetch-Dest' => 'document', 241 'Sec-Fetch-Mode' => 'navigate', 242 'Sec-Fetch-Site' => 'none', 243 'Cache-Control' => 'no-cache', 244 ] 245 ]); 246 247 // ネットワークエラーの場合 248 if (is_wp_error($response)) { 249 $error_message = $response->get_error_message(); 250 $error_code = 'network_error'; 251 $status_code = 500; 252 253 // タイムアウトエラーの場合は特別な処理 254 if (strpos($error_message, 'timeout') !== false || strpos($error_message, 'timed out') !== false) { 255 $error_code = 'timeout_error'; 256 $error_message = 'サーバーの応答が遅すぎます。しばらく時間をおいてから再度お試しください。'; 257 $status_code = 408; // Request Timeout 258 } elseif (strpos($error_message, 'Connection refused') !== false) { 259 $error_code = 'connection_refused'; 260 $error_message = 'サーバーに接続できませんでした。'; 261 $status_code = 503; // Service Unavailable 262 } elseif (strpos($error_message, 'SSL') !== false || strpos($error_message, 'certificate') !== false) { 263 $error_code = 'ssl_error'; 264 $error_message = 'SSL証明書の検証に失敗しました。'; 265 $status_code = 495; // SSL Certificate Error 266 } 267 268 // デバッグ用ログ 269 error_log('wpbc_fetch_metadata network error for URL: ' . $url . ' - Error: ' . $error_message . ' - Code: ' . $error_code); 270 return new WP_Error($error_code, $error_message, ['status' => $status_code]); 271 } 272 273 // HTTPステータスコードを取得 274 $response_code = wp_remote_retrieve_response_code($response); 275 276 // 200以外の場合はエラーを返す 277 if ($response_code !== 200) { 278 $error_message = 'HTTPエラーが発生しました'; 279 switch ($response_code) { 280 case 404: 281 $error_message = 'ページが見つかりません (404)'; 282 break; 283 case 403: 284 $error_message = 'アクセスが拒否されました (403)'; 285 break; 286 case 500: 287 $error_message = 'サーバーエラーが発生しました (500)'; 288 break; 289 case 503: 290 $error_message = 'サービスが利用できません (503)'; 291 break; 292 default: 293 $error_message = "HTTPエラーが発生しました ({$response_code})"; 294 break; 295 } 296 // デバッグ用ログ 297 error_log('wpbc_fetch_metadata HTTP error for URL: ' . $url . ' - Status: ' . $response_code . ' - Message: ' . $error_message); 298 return new WP_Error('http_error', $error_message, ['status' => $response_code]); 299 } 300 301 if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) { 302 $body = wp_remote_retrieve_body($response); 303 304 // リダイレクト後の最終URLを取得 305 // wp_remote_get()は自動的にリダイレクトを追跡するので、 306 // レスポンスのURLを取得する 307 $response_url = wp_remote_retrieve_header($response, 'url'); 308 if ($response_url) { 309 $final_url = $response_url; 310 } else { 311 // ヘッダーから取得できない場合は元のURLを使用 312 $final_url = $url; 313 } 314 315 // 最終URLのドメインとURLを更新 316 $metadata['domain'] = parse_url($final_url, PHP_URL_HOST); 317 $metadata['final_url'] = $final_url; 318 319 // HTMLをパースしてメタデータを抽出 320 $dom = new DOMDocument(); 321 @$dom->loadHTML(mb_convert_encoding($body, 'HTML-ENTITIES', 'UTF-8')); 322 $xpath = new DOMXPath($dom); 323 324 // タイトルを取得 325 $title_nodes = $xpath->query('//title'); 326 if ($title_nodes->length > 0) { 327 $metadata['title'] = trim($title_nodes->item(0)->textContent); 328 } 329 330 // OGPメタタグを取得 331 $og_title = $xpath->query('//meta[@property="og:title"]/@content'); 332 if ($og_title->length > 0) { 333 $metadata['title'] = trim($og_title->item(0)->textContent); 334 } 335 336 // 説明文を取得 337 $description_nodes = $xpath->query('//meta[@name="description"]/@content'); 338 if ($description_nodes->length > 0) { 339 $metadata['description'] = trim($description_nodes->item(0)->textContent); 340 } 341 342 $og_description = $xpath->query('//meta[@property="og:description"]/@content'); 343 if ($og_description->length > 0) { 344 $metadata['description'] = trim($og_description->item(0)->textContent); 345 } 346 347 // サムネイル画像を取得 348 $og_image = $xpath->query('//meta[@property="og:image"]/@content'); 349 if ($og_image->length > 0) { 350 $metadata['thumbnail'] = trim($og_image->item(0)->textContent); 351 } 352 353 // ファビコンを取得 354 $favicon_url = wpbc_get_favicon_url($xpath, $url); 355 if ($favicon_url) { 356 $metadata['favicon'] = $favicon_url; 357 } 358 } 359 360 // 新しく取得した場合はcachedフラグをfalseに設定 361 $metadata['cached'] = false; 362 363 // キャッシュに保存(24時間) 364 set_transient($cache_key, $metadata, 24 * HOUR_IN_SECONDS); 365 366 return $metadata; 227 function wpbc_fetch_metadata( $url ) { 228 // キャッシュキーを生成 229 $cache_key = 'wpbc_metadata_' . md5( $url ); 230 231 // キャッシュから取得を試行 232 $cached_data = get_transient( $cache_key ); 233 if ( false !== $cached_data ) { 234 // キャッシュから取得した場合はcachedフラグを追加 235 $cached_data['cached'] = true; 236 return $cached_data; 237 } 238 239 // URLの検証 240 if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { 241 return new WP_Error( 'invalid_url', '無効なURL形式です。', array( 'status' => 400 ) ); 242 } 243 244 // 内部URLの場合は内部メタデータ取得関数を使用 245 if ( wpbc_is_internal_url( $url ) ) { 246 $metadata = wpbc_fetch_internal_metadata( $url ); 247 if ( is_wp_error( $metadata ) ) { 248 return $metadata; 249 } 250 251 // キャッシュに保存(24時間) 252 set_transient( $cache_key, $metadata, 24 * HOUR_IN_SECONDS ); 253 254 return $metadata; 255 } 256 257 $metadata = array( 258 'title' => '', 259 'description' => '', 260 'thumbnail' => '', 261 'domain' => parse_url( $url, PHP_URL_HOST ), 262 'final_url' => $url, // リダイレクト後の最終URL 263 ); 264 265 // HTTPリクエストでメタデータを取得(リダイレクトを追跡) 266 $response = wp_remote_get( 267 $url, 268 array( 269 'timeout' => 20, // タイムアウトを20秒に延長 270 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 271 'redirection' => 5, // 最大5回までリダイレクトを追跡 272 'sslverify' => false, // SSL証明書の検証を無効化(テスト用) 273 'headers' => array( 274 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 275 'Accept-Language' => 'ja-JP,ja;q=0.9,en;q=0.8', 276 'Accept-Encoding' => 'gzip, deflate, br', 277 'Connection' => 'keep-alive', 278 'Upgrade-Insecure-Requests' => '1', 279 'Sec-Fetch-Dest' => 'document', 280 'Sec-Fetch-Mode' => 'navigate', 281 'Sec-Fetch-Site' => 'none', 282 'Cache-Control' => 'no-cache', 283 ), 284 ) 285 ); 286 287 // ネットワークエラーの場合 288 if ( is_wp_error( $response ) ) { 289 $error_message = $response->get_error_message(); 290 $error_code = 'network_error'; 291 $status_code = 500; 292 293 // タイムアウトエラーの場合は特別な処理 294 if ( false !== strpos( $error_message, 'timeout' ) || false !== strpos( $error_message, 'timed out' ) ) { 295 $error_code = 'timeout_error'; 296 $error_message = 'サーバーの応答が遅すぎます。しばらく時間をおいてから再度お試しください。'; 297 $status_code = 408; // Request Timeout 298 } elseif ( false !== strpos( $error_message, 'Connection refused' ) ) { 299 $error_code = 'connection_refused'; 300 $error_message = 'サーバーに接続できませんでした。'; 301 $status_code = 503; // Service Unavailable 302 } elseif ( false !== strpos( $error_message, 'SSL' ) || false !== strpos( $error_message, 'certificate' ) ) { 303 $error_code = 'ssl_error'; 304 $error_message = 'SSL証明書の検証に失敗しました。'; 305 $status_code = 495; // SSL Certificate Error 306 } 307 308 // デバッグ用ログ 309 error_log( 'wpbc_fetch_metadata network error for URL: ' . $url . ' - Error: ' . $error_message . ' - Code: ' . $error_code ); 310 return new WP_Error( $error_code, $error_message, array( 'status' => $status_code ) ); 311 } 312 313 // HTTPステータスコードを取得 314 $response_code = wp_remote_retrieve_response_code( $response ); 315 316 // 200以外の場合はエラーを返す 317 if ( 200 !== $response_code ) { 318 $error_message = 'HTTPエラーが発生しました'; 319 switch ( $response_code ) { 320 case 404: 321 $error_message = 'ページが見つかりません (404)'; 322 break; 323 case 403: 324 $error_message = 'アクセスが拒否されました (403)'; 325 break; 326 case 500: 327 $error_message = 'サーバーエラーが発生しました (500)'; 328 break; 329 case 503: 330 $error_message = 'サービスが利用できません (503)'; 331 break; 332 default: 333 $error_message = "HTTPエラーが発生しました ({$response_code})"; 334 break; 335 } 336 // デバッグ用ログ 337 error_log( 'wpbc_fetch_metadata HTTP error for URL: ' . $url . ' - Status: ' . $response_code . ' - Message: ' . $error_message ); 338 return new WP_Error( 'http_error', $error_message, array( 'status' => $response_code ) ); 339 } 340 341 if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { 342 $body = wp_remote_retrieve_body( $response ); 343 344 // リダイレクト後の最終URLを取得 345 // wp_remote_get()は自動的にリダイレクトを追跡するので、 346 // レスポンスのURLを取得する 347 $response_url = wp_remote_retrieve_header( $response, 'url' ); 348 if ( $response_url ) { 349 $final_url = $response_url; 350 } else { 351 // ヘッダーから取得できない場合は元のURLを使用 352 $final_url = $url; 353 } 354 355 // 最終URLのドメインとURLを更新 356 $metadata['domain'] = parse_url( $final_url, PHP_URL_HOST ); 357 $metadata['final_url'] = $final_url; 358 359 // HTMLをパースしてメタデータを抽出 360 $dom = new DOMDocument(); 361 $libxml_previous_state = libxml_use_internal_errors( true ); 362 $dom->loadHTML( mb_convert_encoding( $body, 'HTML-ENTITIES', 'UTF-8' ) ); 363 libxml_clear_errors(); 364 libxml_use_internal_errors( $libxml_previous_state ); 365 $xpath = new DOMXPath( $dom ); 366 367 // タイトルを取得 368 $title_nodes = $xpath->query( '//title' ); 369 if ( $title_nodes->length > 0 ) { 370 $metadata['title'] = trim( $title_nodes->item( 0 )->textContent ); 371 } 372 373 // OGPメタタグを取得 374 $og_title = $xpath->query( '//meta[@property="og:title"]/@content' ); 375 if ( $og_title->length > 0 ) { 376 $metadata['title'] = trim( $og_title->item( 0 )->textContent ); 377 } 378 379 // 説明文を取得 380 $description_nodes = $xpath->query( '//meta[@name="description"]/@content' ); 381 if ( $description_nodes->length > 0 ) { 382 $metadata['description'] = trim( $description_nodes->item( 0 )->textContent ); 383 } 384 385 $og_description = $xpath->query( '//meta[@property="og:description"]/@content' ); 386 if ( $og_description->length > 0 ) { 387 $metadata['description'] = trim( $og_description->item( 0 )->textContent ); 388 } 389 390 // サムネイル画像を取得 391 $og_image = $xpath->query( '//meta[@property="og:image"]/@content' ); 392 if ( $og_image->length > 0 ) { 393 $metadata['thumbnail'] = trim( $og_image->item( 0 )->textContent ); 394 } 395 396 // ファビコンを取得 397 $favicon_url = wpbc_get_favicon_url( $xpath, $url ); 398 if ( $favicon_url ) { 399 $metadata['favicon'] = $favicon_url; 400 } 401 } 402 403 // 新しく取得した場合はcachedフラグをfalseに設定 404 $metadata['cached'] = false; 405 406 // キャッシュに保存(24時間) 407 set_transient( $cache_key, $metadata, 24 * HOUR_IN_SECONDS ); 408 409 return $metadata; 367 410 } 368 411 … … 370 413 * サイト内検索のREST APIコールバック 371 414 */ 372 function wpbc_search_posts( $request) {373 $query = $request->get_param('q');374 375 if (empty($query)) {376 return new WP_Error('missing_query', '検索クエリが指定されていません。', ['status' => 400]);377 }378 379 // 投稿と固定ページを検索380 $args = [ 381 'post_type' => ['post', 'page'],382 'post_status'=> 'publish',383 's'=> $query,384 'posts_per_page' => -1,385 'orderby'=> 'relevance',386 'order' => 'DESC' 387 ];388 389 $posts = get_posts($args);390 $results = [];391 392 // 投稿を先に並べる393 $posts_sorted = [];394 $pages_sorted = [];395 396 foreach ($posts as $post) {397 if (get_post_type($post->ID) === 'post') {398 $posts_sorted[] = $post;399 } else {400 $pages_sorted[] = $post;401 }402 }403 404 // 投稿と固定ページを結合(投稿を優先)405 $sorted_posts = array_merge($posts_sorted, $pages_sorted);406 407 foreach ($sorted_posts as $post) {408 $results[] = [ 409 'id'=> $post->ID,410 'title' => get_the_title($post->ID),411 'url' => get_permalink($post->ID),412 'type' => get_post_type($post->ID),413 'date' => get_the_date('Y-m-d', $post->ID),414 'thumbnail' => get_the_post_thumbnail_url($post->ID, 'thumbnail') 415 ];416 }417 418 return [ 419 'success' => true,420 'data' => $results 421 ];415 function wpbc_search_posts( $request ) { 416 $query = $request->get_param( 'q' ); 417 418 if ( empty( $query ) ) { 419 return new WP_Error( 'missing_query', '検索クエリが指定されていません。', array( 'status' => 400 ) ); 420 } 421 422 // 投稿と固定ページを検索 423 $args = array( 424 'post_type' => array( 'post', 'page' ), 425 'post_status' => 'publish', 426 's' => $query, 427 'posts_per_page' => -1, 428 'orderby' => 'relevance', 429 'order' => 'DESC', 430 ); 431 432 $posts = get_posts( $args ); 433 $results = array(); 434 435 // 投稿を先に並べる 436 $posts_sorted = array(); 437 $pages_sorted = array(); 438 439 foreach ( $posts as $post ) { 440 if ( 'post' === get_post_type( $post->ID ) ) { 441 $posts_sorted[] = $post; 442 } else { 443 $pages_sorted[] = $post; 444 } 445 } 446 447 // 投稿と固定ページを結合(投稿を優先) 448 $sorted_posts = array_merge( $posts_sorted, $pages_sorted ); 449 450 foreach ( $sorted_posts as $post ) { 451 $results[] = array( 452 'id' => $post->ID, 453 'title' => get_the_title( $post->ID ), 454 'url' => get_permalink( $post->ID ), 455 'type' => get_post_type( $post->ID ), 456 'date' => get_the_date( 'Y-m-d', $post->ID ), 457 'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'thumbnail' ), 458 ); 459 } 460 461 return array( 462 'success' => true, 463 'data' => $results, 464 ); 422 465 } 423 466 … … 425 468 * 内部サイトのメタデータを取得する 426 469 */ 427 function wpbc_fetch_internal_metadata( $url) {428 // URLから投稿IDを取得429 $post_id = url_to_postid($url);430 431 if (!$post_id) {432 return new WP_Error('post_not_found', '指定されたURLの投稿が見つかりません。', ['status' => 404]);433 }434 435 // 投稿のタイトルを取得436 $title = get_the_title($post_id);437 438 // 投稿の抜粋を取得439 $excerpt = get_the_excerpt($post_id);440 441 // サムネイル画像を取得(thumbnailサイズ)442 $thumbnail_id = get_post_thumbnail_id($post_id);443 $thumbnail_url = '';444 if ($thumbnail_id) {445 $thumbnail_url = wp_get_attachment_image_url($thumbnail_id, 'thumbnail');446 }447 448 return [ 449 'title'=> $title,450 'description' => $excerpt,451 'thumbnail'=> $thumbnail_url,452 'url'=> $url,453 'cached' => false 454 ];470 function wpbc_fetch_internal_metadata( $url ) { 471 // URLから投稿IDを取得 472 $post_id = url_to_postid( $url ); 473 474 if ( ! $post_id ) { 475 return new WP_Error( 'post_not_found', '指定されたURLの投稿が見つかりません。', array( 'status' => 404 ) ); 476 } 477 478 // 投稿のタイトルを取得 479 $title = get_the_title( $post_id ); 480 481 // 投稿の抜粋を取得 482 $excerpt = get_the_excerpt( $post_id ); 483 484 // サムネイル画像を取得(thumbnailサイズ) 485 $thumbnail_id = get_post_thumbnail_id( $post_id ); 486 $thumbnail_url = ''; 487 if ( $thumbnail_id ) { 488 $thumbnail_url = wp_get_attachment_image_url( $thumbnail_id, 'thumbnail' ); 489 } 490 491 return array( 492 'title' => $title, 493 'description' => $excerpt, 494 'thumbnail' => $thumbnail_url, 495 'url' => $url, 496 'cached' => false, 497 ); 455 498 } 456 499 … … 458 501 * キャッシュクリアのREST APIコールバック 459 502 */ 460 function wpbc_clear_cache( $request) {461 global $wpdb;462 463 $type = $request->get_param('type');464 $cleared_count = 0;465 466 if ($type === 'all') {467 // 全てのWordPressキャッシュをクリア468 $result= $wpdb->query(469 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'"470 );471 $cleared_count = $result;472 473 // オブジェクトキャッシュもクリア474 if (function_exists('wp_cache_flush')) {475 wp_cache_flush();476 }477 478 $message = '全てのキャッシュをクリアしました。';479 } else {480 // プラグインのキャッシュのみをクリア481 $result= $wpdb->query(482 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wpbc_metadata_%' OR option_name LIKE '_transient_timeout_wpbc_metadata_%'"483 );484 $cleared_count = $result;485 486 $message = 'ブログカードのキャッシュをクリアしました。';487 }488 489 return [ 490 'success'=> true,491 'message'=> $message,492 'cleared_count' => $cleared_count 493 ];503 function wpbc_clear_cache( $request ) { 504 global $wpdb; 505 506 $type = $request->get_param( 'type' ); 507 $cleared_count = 0; 508 509 if ( 'all' === $type ) { 510 // 全てのWordPressキャッシュをクリア 511 $result = $wpdb->query( 512 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'" 513 ); 514 $cleared_count = $result; 515 516 // オブジェクトキャッシュもクリア 517 if ( function_exists( 'wp_cache_flush' ) ) { 518 wp_cache_flush(); 519 } 520 521 $message = '全てのキャッシュをクリアしました。'; 522 } else { 523 // プラグインのキャッシュのみをクリア 524 $result = $wpdb->query( 525 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wpbc_metadata_%' OR option_name LIKE '_transient_timeout_wpbc_metadata_%'" 526 ); 527 $cleared_count = $result; 528 529 $message = 'ブログカードのキャッシュをクリアしました。'; 530 } 531 532 return array( 533 'success' => true, 534 'message' => $message, 535 'cleared_count' => $cleared_count, 536 ); 494 537 } 495 538 … … 497 540 * ファビコンを取得する関数 498 541 */ 499 function wpbc_get_favicon_url($xpath, $base_url) { 500 $favicon_url = null; 501 502 // ファビコンのURLを取得するクエリ(優先順位順) 503 $queries = [ 504 "//link[@rel='icon' and @type='image/svg+xml']", 505 "//link[@rel='icon' and @type='image/png']", 506 "//link[@rel='shortcut icon']", 507 "//link[contains(@rel, 'icon')]", 508 "//link[@rel='apple-touch-icon']", 509 "//meta[@name='msapplication-TileImage']" 510 ]; 511 512 foreach ($queries as $query) { 513 $results = $xpath->query($query); 514 foreach ($results as $item) { 515 if ($item instanceof DOMElement) { 516 $attr = ($item->tagName == 'meta') ? 'content' : 'href'; 517 $temp_url = $item->getAttribute($attr); 518 519 // URLを絶対URLに変換 520 $temp_url = wpbc_convert_to_absolute_url($temp_url, $base_url); 521 522 // ファビコンのURLが有効かチェック 523 if (wpbc_check_favicon_exists($temp_url)) { 524 return $temp_url; 525 } 526 } 527 } 528 } 529 530 // ドメイン直下のファビコンを探す 531 $default_favicons = [ 532 wpbc_convert_to_absolute_url("/favicon.svg", $base_url), 533 wpbc_convert_to_absolute_url("/favicon.ico", $base_url) 534 ]; 535 536 foreach ($default_favicons as $favicon_url) { 537 if (wpbc_check_favicon_exists($favicon_url)) { 538 return $favicon_url; 539 } 540 } 541 542 return null; 542 function wpbc_get_favicon_url( $xpath, $base_url ) { 543 $favicon_url = null; 544 545 // ファビコンのURLを取得するクエリ(優先順位順) 546 $queries = array( 547 "//link[@rel='icon' and @type='image/svg+xml']", 548 "//link[@rel='icon' and @type='image/png']", 549 "//link[@rel='shortcut icon']", 550 "//link[contains(@rel, 'icon')]", 551 "//link[@rel='apple-touch-icon']", 552 "//meta[@name='msapplication-TileImage']", 553 ); 554 555 foreach ( $queries as $query ) { 556 $results = $xpath->query( $query ); 557 foreach ( $results as $item ) { 558 if ( $item instanceof DOMElement ) { 559 $attr = $item->getAttribute( 'href' ); 560 if ( empty( $attr ) ) { 561 $attr = $item->getAttribute( 'content' ); 562 } 563 $temp_url = $attr; 564 565 // URLを絶対URLに変換 566 $temp_url = wpbc_convert_to_absolute_url( $temp_url, $base_url ); 567 568 // ファビコンのURLが有効かチェック 569 if ( wpbc_check_favicon_exists( $temp_url ) ) { 570 return $temp_url; 571 } 572 } 573 } 574 } 575 576 // ドメイン直下のファビコンを探す 577 $default_favicons = array( 578 wpbc_convert_to_absolute_url( '/favicon.svg', $base_url ), 579 wpbc_convert_to_absolute_url( '/favicon.ico', $base_url ), 580 ); 581 582 foreach ( $default_favicons as $favicon_url ) { 583 if ( wpbc_check_favicon_exists( $favicon_url ) ) { 584 return $favicon_url; 585 } 586 } 587 588 return null; 543 589 } 544 590 … … 546 592 * 相対URLを絶対URLに変換する 547 593 */ 548 function wpbc_convert_to_absolute_url( $url, $base_url) {549 if (preg_match('/^https?:\/\//', $url)) {550 return $url;551 }552 553 $parts = parse_url($base_url);554 $scheme = $parts['scheme'];555 $host= $parts['host'];556 557 if (strpos($url, '/') === 0) {558 return "$scheme://$host$url";559 }560 561 $base_path = isset($parts['path']) ? dirname($parts['path']) : '';562 return "$scheme://$host" . rtrim($base_path, '/') . '/' . ltrim($url, '/');594 function wpbc_convert_to_absolute_url( $url, $base_url ) { 595 if ( preg_match( '/^https?:\/\//', $url ) ) { 596 return $url; 597 } 598 599 $parts = parse_url( $base_url ); 600 $scheme = $parts['scheme']; 601 $host = $parts['host']; 602 603 if ( 0 === strpos( $url, '/' ) ) { 604 return "$scheme://$host$url"; 605 } 606 607 $base_path = isset( $parts['path'] ) ? dirname( $parts['path'] ) : ''; 608 return "$scheme://$host" . rtrim( $base_path, '/' ) . '/' . ltrim( $url, '/' ); 563 609 } 564 610 … … 566 612 * ファビコンが存在するかチェックする 567 613 */ 568 function wpbc_check_favicon_exists( $url) {569 $headers = @get_headers($url, 1);570 return $headers && strpos($headers[0], '200 OK') !== false;571 } 614 function wpbc_check_favicon_exists( $url ) { 615 $headers = get_headers( $url, 1 ); 616 return $headers && false !== strpos( $headers[0], '200 OK' ); 617 } -
blogcard-for-wp/tags/2.0.6/build/block.json
r3357123 r3418932 5 5 "version": "2.0.0", 6 6 "title": "ブログカード", 7 "category": " text",7 "category": "su-blocks", 8 8 "icon": "admin-links", 9 9 "description": "URLを入力してブログカードを生成するブロックです。", -
blogcard-for-wp/tags/2.0.6/build/index-rtl.css
r3357123 r3418932 1 .wp-blo gcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;right:0;overflow-y:auto;position:absolute;left:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-right:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-left:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{display:-webkit-box;overflow:hidden;-webkit-line-clamp:2;color:#666;font-size:12px;line-height:1.4;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto}1 .wp-block-blogcard.is-selected{padding:1rem}.wp-block-blogcard:not(.is-selected){& .wp-blogcard{margin:0!important}}.wp-blogcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;right:0;overflow-y:auto;position:absolute;left:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-right:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-left:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{color:#666;display:-webkit-box;font-size:12px;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto} -
blogcard-for-wp/tags/2.0.6/build/index.asset.php
r3357123 r3418932 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => ' 1e9fb9c52b6f2f7e3826');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'e395c5c79d9305558fa9'); -
blogcard-for-wp/tags/2.0.6/build/index.css
r3357123 r3418932 1 .wp-blo gcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;left:0;overflow-y:auto;position:absolute;right:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-left:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-right:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{display:-webkit-box;overflow:hidden;-webkit-line-clamp:2;color:#666;font-size:12px;line-height:1.4;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto}1 .wp-block-blogcard.is-selected{padding:1rem}.wp-block-blogcard:not(.is-selected){& .wp-blogcard{margin:0!important}}.wp-blogcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;left:0;overflow-y:auto;position:absolute;right:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-left:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-right:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{color:#666;display:-webkit-box;font-size:12px;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto} -
blogcard-for-wp/tags/2.0.6/build/index.js
r3357123 r3418932 1 (()=>{"use strict";var e,t={ 618:(e,t,n)=>{const l=window.wp.blocks,r=window.React,a=window.wp.blockEditor,c=window.wp.element,o=window.wp.i18n,s=window.wp.apiFetch;var i=n.n(s);const p=window.wp.components;function u(e){return!(!e||e.length<10)&&URL.canParse(e)}function m(e){if(!e)return!1;try{const t=new URL(e),n=new URL(window.location.href).hostname,l=t.hostname;return n===l||!(!n.endsWith("."+l)&&!l.endsWith("."+n))}catch{return!1}}function d({results:e,isLoading:t,onSelect:n,onClose:l,error:a}){return(0,r.createElement)("div",{className:"wpbc-search-results"},(0,r.createElement)("div",{className:"wpbc-search-results-header"},(0,r.createElement)("span",null,(0,o.__)("検索結果","wpbc")),(0,r.createElement)("button",{type:"button",className:"wpbc-search-close",onClick:l},"×")),t?(0,r.createElement)("div",{className:"wpbc-search-loading"},(0,r.createElement)(p.Spinner,null),(0,o.__)("検索中...","wpbc")):a?(0,r.createElement)("div",{className:"wpbc-search-error",style:{color:"#cc0000",padding:"10px",textAlign:"center"}},a):e&&e.length>0?(0,r.createElement)("ul",{className:"wpbc-search-results-list"},e.map(e=>(0,r.createElement)("li",{key:e.id,className:"wpbc-search-result-item",onClick:()=>n(e)},e.thumbnail&&(0,r.createElement)("div",{className:"wpbc-search-result-thumbnail"},(0,r.createElement)("img",{src:e.thumbnail,alt:e.title,width:"60",height:"60"})),(0,r.createElement)("div",{className:"wpbc-search-result-content"},(0,r.createElement)("div",{className:"wpbc-search-result-title"},e.title),(0,r.createElement)("div",{className:"wpbc-search-result-meta"},(0,r.createElement)("span",{className:"wpbc-search-result-type"},"post"===e.type?(0,o.__)("投稿","wpbc"):(0,o.__)("固定ページ","wpbc")),(0,r.createElement)("span",{className:"wpbc-search-result-date"},e.date)),e.excerpt&&(0,r.createElement)("div",{className:"wpbc-search-result-excerpt"},e.excerpt))))):(0,r.createElement)("div",{className:"wpbc-search-no-results"},(0,o.__)("検索結果が見つかりませんでした","wpbc")))}function b({value:e,onChange:t,onKeyDown:n,onSelectResult:l,placeholder:a=(0,o.__)("https://example.com or キーワード","wpbc"),label:s=(0,o.__)("URLまたはサイト内検索","wpbc"),showSearch:m=!1,disabled:b=!1}){const h=u(e),[w,g]=(0,c.useState)([]),[_,f]=(0,c.useState)(!1),[E,v]=(0,c.useState)(!1),[y,C]=(0,c.useState)(""),x=(0,c.useRef)(null),k=(0,c.useCallback)(async e=>{if(!e||e.length<2)return g([]),v(!1),void C("");x.current&&x.current.abort(),x.current=new AbortController,f(!0),C("");try{const t=await i()({path:`/wpbc/v1/search?q=${encodeURIComponent(e)}`,method:"GET",signal:x.current.signal});t&&t.success?(g(t.data||[]),v(!0),C("")):(g([]),v(!1),C((0,o.__)("検索に失敗しました。","wpbc")))}catch(e){if("AbortError"===e.name)return;g([]),v(!1),C((0,o.__)("検索中にエラーが発生しました。","wpbc"))}finally{f(!1)}},[]);return(0,c.useEffect)(()=>{if(m&&!b){if(!e||""===e.trim())return g([]),v(!1),void C("");if(!h){const t=setTimeout(()=>{k(e)},300);return()=>{clearTimeout(t),x.current&&x.current.abort()}}g([]),v(!1),C("")}},[e,m,b,h,k]),(0,c.useEffect)(()=>{h&&(g([]),v(!1),f(!1),C(""))},[h]),(0,c.useEffect)(()=>()=>{x.current&&x.current.abort()},[]),(0,r.createElement)("div",{style:{position:"relative"}},(0,r.createElement)(p.TextControl,{label:s,value:e,onChange:t,onKeyDown:t=>{"Enter"===t.key&&(t.preventDefault(),u(e)&&n&&n(t))},placeholder:a}),m&&!b&&e&&""!==e.trim()&&!u(e)&&(E||y)&&(0,r.createElement)(d,{results:w,isLoading:_,onSelect:e=>{t(e.url),l&&l(e),v(!1),g([])},onClose:()=>{v(!1),g([])},error:y}))}function h({attributes:e}){const{url:t,title:n,description:l,thumbnailUrl:a,thumbnailId:c,showThumbnail:o,favicon:s,target:i="_blank",noopener:p=!0,nofollow:u=!1,noreferrer:m=!1,sponsored:d=!1,ugc:b=!1,isSelected:h=!1}=e,w=[];p&&w.push("noopener"),m&&w.push("noreferrer"),u&&w.push("nofollow"),d&&w.push("sponsored"),b&&w.push("ugc");const g=w.length>0?w.join(" "):"",_=a,f=o&&_;return(0,r.createElement)("article",{className:"wp-blogcard",cite:t},(0,r.createElement)("a",{href:t,target:i||void 0,rel:g||void 0,className:"wp-blogcard-item"},f&&(0,r.createElement)("figure",{className:"wp-blogcard-figure"},(0,r.createElement)("img",{src:_,alt:"","aria-hidden":"true"})),(0,r.createElement)("div",{className:"wp-blogcard-content"},(0,r.createElement)("div",{className:"wp-blogcard-title"},n),(0,r.createElement)("div",{className:"wp-blogcard-description"},l),(0,r.createElement)("div",{className:"wp-blogcard-cite"},s&&(0,r.createElement)("img",{className:"wp-blogcard-favicon",src:s,alt:"","aria-hidden":"true"}),(0,r.createElement)("div",{className:"wp-blogcard-domain"},function(e){try{return new URL(e).hostname}catch{return""}}(t))))))}function w({url:e,cached:t,onOpenLink:n}){return(0,r.createElement)("div",{className:"blogcard-preview-footer",style:{marginTop:"10px",display:"flex",justifyContent:"space-between",alignItems:"center"}},(0,r.createElement)("div",{style:{fontSize:"12px",color:"#666",display:"flex",alignItems:"center",gap:"4px"}},t&&(0,r.createElement)("span",{style:{padding:"2px 6px",borderRadius:"3px",fontSize:"10px",fontWeight:"bold"}},"CACHED")),(0,r.createElement)(p.Button,{type:"button",className:"wpbc-show-link-button",onClick:()=>n(e),disabled:!e,variant:"secondary",size:"small"},(0,o.__)("リンク先を表示","wpbc")))}function g({error:e,errorCode:t}){return e?(0,r.createElement)("div",{style:{color:"#cc0000",fontSize:"14px",margin:"8px 0",padding:"8px 0"}},e,t&&(0,r.createElement)("span",{style:{marginLeft:"8px",color:"#666",fontSize:"12px"}},"(",t,")")):null}function _({message:e=(0,o.__)("メタデータを取得中...","wpbc")}){return(0,r.createElement)("div",{style:{textAlign:"center",padding:"1.25rem"}},(0,r.createElement)(p.Spinner,null),(0,r.createElement)("p",{style:{fontSize:"0.875rem"}},e))}function f({attributes:e,setAttributes:t,inputUrl:n,setInputUrl:l,handleKeyDown:c}){const{target:s,noopener:i,nofollow:u,noreferrer:m,sponsored:d,ugc:b,showThumbnail:h,title:w,description:g,thumbnailUrl:_,thumbnailId:f}=e,v=()=>f&&_?(0,r.createElement)("div",{style:{width:"100%",aspectRatio:"16/9",overflow:"hidden",borderRadius:"4px",backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center"}},(0,r.createElement)("img",{src:_,alt:"",style:{width:"100%",height:"100%",objectFit:"cover"}})):(0,r.createElement)("div",{style:{backgroundColor:"#f0f0f0",borderRadius:"4px",display:"flex",alignItems:"center",justifyContent:"center",color:"#666",fontSize:"14px"}},(0,o.__)("サムネイルを設定","wpbc"));return(0,r.createElement)(a.InspectorControls,null,(0,r.createElement)(p.PanelBody,{title:(0,o.__)("ブロック設定","wpbc"),initialOpen:!0},(0,r.createElement)(p.TextControl,{label:(0,o.__)("URL","wpbc"),value:n,onChange:e=>l(e),onKeyDown:c,placeholder:(0,o.__)("https://example.com or キーワード","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("TARGET属性","wpbc")),(0,r.createElement)(p.SelectControl,{label:(0,o.__)("TARGET属性","wpbc"),value:s,onChange:e=>{t("_blank"===e?{target:e,noopener:!0}:{target:e})},options:[{label:(0,o.__)("なし","wpbc"),value:""},{label:(0,o.__)("_blank(別タブ)","wpbc"),value:"_blank"},{label:(0,o.__)("_self (同じタブ)","wpbc"),value:"_self"}]}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("Rel属性","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("noopener を追加","wpbc"),checked:i,disabled:"_blank"===s,onChange:e=>t({noopener:e})}),"_blank"===s&&(0,r.createElement)("p",{style:{fontSize:"12px",color:"#666",margin:"8px 0"}},(0,o.__)("_blankの場合はセキュリティ上noopenerが必須です","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=nofollow を追加","wpbc"),checked:u,onChange:e=>t({nofollow:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=noreferrer を追加","wpbc"),checked:m,onChange:e=>t({noreferrer:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=sponsored を追加","wpbc"),checked:d,onChange:e=>t({sponsored:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=ugc を追加","wpbc"),checked:b,onChange:e=>t({ugc:e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("サムネイル","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("サムネイルを表示しない","wpbc"),checked:!h,onChange:e=>t({showThumbnail:!e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("タイトルを手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("タイトル","wpbc"),value:w,onChange:e=>t({title:e}),placeholder:(0,o.__)("タイトルを入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("説明文を手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("説明文","wpbc"),value:g,onChange:e=>t({description:e}),placeholder:(0,o.__)("説明文を入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)(p.BaseControl,{label:(0,o.__)("サムネイルを手動で設定","wpbc")},(0,r.createElement)(a.MediaUploadCheck,null,(0,r.createElement)(a.MediaUpload,{onSelect:e=>{t({thumbnailId:e.id,thumbnailUrl:e.url,showThumbnail:!0})},allowedTypes:["image"],value:f,render:({open:e})=>(0,r.createElement)(p.Button,{onClick:e,className:"editor-post-featured-image__toggle"},(0,r.createElement)(v,null))})),(0,r.createElement)(p.Button,{style:{marginTop:"0.5rem"},className:"is-tertiary",onClick:()=>{t({thumbnailId:0,thumbnailUrl:"",showThumbnail:!1})}},(0,o.__)("クリア","wpbc"))),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("キャッシュ管理","wpbc")),(0,r.createElement)("div",{style:{display:"flex",gap:"10px",flexWrap:"wrap"}},(0,r.createElement)(p.Button,{secondary:!0,onClick:E("plugin"),style:{fontSize:"12px"}},(0,o.__)("ブログカードキャッシュクリア","wpbc")),(0,r.createElement)(p.Button,{isDestructive:!0,onClick:()=>{confirm((0,o.__)("全てのWordPressキャッシュをクリアしますか?","wpbc"))&&E("all")()},style:{fontSize:"12px"}},(0,o.__)("全キャッシュクリア","wpbc")))))}function E(e="plugin"){return async()=>{try{const t=await i()({path:"/wpbc/v1/clear-cache",method:"POST",data:{type:e}});t.success?alert(t.message):alert((0,o.__)("キャッシュのクリアに失敗しました。","wpbc"))}catch(e){console.error("Cache clear error:",e),alert((0,o.__)("キャッシュのクリア中にエラーが発生しました。","wpbc"))}}}(0,l.registerBlockType)("su/blogcard",{edit:function({attributes:e,setAttributes:t}){const{url:n,target:l,noopener:s,nofollow:p,noreferrer:d,sponsored:E,ugc:v,showThumbnail:y,title:C,description:x,thumbnailUrl:k,thumbnailId:T,favicon:N}=e,[S,R]=(0,c.useState)(n||""),[U,O]=(0,c.useState)(!1),[I,D]=(0,c.useState)(""),[L,z]=(0,c.useState)(""),[A,B]=(0,c.useState)(!1),j=(0,a.useBlockProps)({className:"wp-block-blogcard"}),K=j.className?.includes("is-selected")||!1,P=e=>{"Enter"===e.key&&(e.preventDefault(),u(S)&&(S!==n&&t({url:"",title:"",description:"",thumbnailUrl:"",showThumbnail:!1,favicon:""}),(async()=>{if(!S||S.trim().length<10||!u(S))return D(""),void z("");O(!0),D(""),z("");try{const e=m(S)?`/wpbc/v1/internal-metadata?url=${encodeURIComponent(S)}`:"/wpbc/v1/metadata",n=await i()({path:e,method:m(S)?"GET":"POST",data:m(S)?void 0:{url:S}});if(n.success){const e=n.data;t({url:S,title:e.title||S,description:e.description||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""}),B(!0===e.cached),D(""),z("")}else{const e=n.message||n.data?.message||(0,o.__)("メタデータの取得に失敗しました。","wpbc"),t=n.code||n.data?.code||"UNKNOWN_ERROR";D(e),z(t)}}catch(e){const t=e.status||e.code||"NETWORK_ERROR";let n=e.message||(0,o.__)("メタデータの取得中にエラーが発生しました。","wpbc");e.data&&e.data.message&&(n=e.data.message),D(n),z(t)}finally{O(!1)}})()))},W=e=>{R(e.url),D(""),z(""),B(!1),t({url:e.url,title:e.title||e.url,description:e.excerpt||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""})};return(0,r.createElement)("div",{...j},(0,r.createElement)(f,{attributes:e,setAttributes:t,inputUrl:S,setInputUrl:R,handleKeyDown:P}),n||I?(0,r.createElement)("div",null,U&&(0,r.createElement)(_,null),K&&(0,r.createElement)(b,{key:"selected-search-input",value:S,onChange:e=>R(e),onKeyDown:P,onSelectResult:W,showSearch:!0,disabled:!1}),(0,r.createElement)(g,{error:I,errorCode:L}),!U&&!I&&n&&(0,r.createElement)("div",{className:"blogcard-preview"},(0,r.createElement)(h,{attributes:{url:n,title:C,description:x,thumbnailUrl:k,showThumbnail:y,favicon:N,isSelected:K}}),K&&(0,r.createElement)(w,{url:n,cached:A,onOpenLink:e=>{window.open(e,"_blank")}}))):(0,r.createElement)(b,{key:"placeholder-search-input",value:S,onChange:e=>R(e),onKeyDown:P,onSelectResult:W,showSearch:!0,disabled:!1}))},save:function({attributes:e}){const t=a.useBlockProps.save();return(0,r.createElement)("div",{...t},(0,r.createElement)(h,{attributes:e}))}})}},n={};function l(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,l),a.exports}l.m=t,e=[],l.O=(t,n,r,a)=>{if(!n){var c=1/0;for(p=0;p<e.length;p++){for(var[n,r,a]=e[p],o=!0,s=0;s<n.length;s++)(!1&a||c>=a)&&Object.keys(l.O).every(e=>l.O[e](n[s]))?n.splice(s--,1):(o=!1,a<c&&(c=a));if(o){e.splice(p--,1);var i=r();void 0!==i&&(t=i)}}return t}a=a||0;for(var p=e.length;p>0&&e[p-1][2]>a;p--)e[p]=e[p-1];e[p]=[n,r,a]},l.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return l.d(t,{a:t}),t},l.d=(e,t)=>{for(var n in t)l.o(t,n)&&!l.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var r,a,[c,o,s]=n,i=0;if(c.some(t=>0!==e[t])){for(r in o)l.o(o,r)&&(l.m[r]=o[r]);if(s)var p=s(l)}for(t&&t(n);i<c.length;i++)a=c[i],l.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return l.O(p)},n=globalThis.webpackChunkblogcard_for_wp=globalThis.webpackChunkblogcard_for_wp||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var r=l.O(void 0,[350],()=>l(618));r=l.O(r)})();1 (()=>{"use strict";var e,t={486:(e,t,n)=>{const l=window.wp.blocks,r=window.React,a=window.wp.blockEditor,c=window.wp.element,o=window.wp.i18n,s=window.wp.apiFetch;var i=n.n(s);const p=window.wp.components;function u(e){return!(!e||e.length<10)&&URL.canParse(e)}function m(e){if(!e)return!1;try{const t=new URL(e),n=new URL(window.location.href).hostname,l=t.hostname;return n===l||!(!n.endsWith("."+l)&&!l.endsWith("."+n))}catch{return!1}}function d({results:e,isLoading:t,onSelect:n,onClose:l,error:a}){return(0,r.createElement)("div",{className:"wpbc-search-results"},(0,r.createElement)("div",{className:"wpbc-search-results-header"},(0,r.createElement)("span",null,(0,o.__)("検索結果","wpbc")),(0,r.createElement)("button",{type:"button",className:"wpbc-search-close",onClick:l},"×")),t?(0,r.createElement)("div",{className:"wpbc-search-loading"},(0,r.createElement)(p.Spinner,null),(0,o.__)("検索中...","wpbc")):a?(0,r.createElement)("div",{className:"wpbc-search-error",style:{color:"#cc0000",padding:"10px",textAlign:"center"}},a):e&&e.length>0?(0,r.createElement)("ul",{className:"wpbc-search-results-list"},e.map(e=>(0,r.createElement)("li",{key:e.id,className:"wpbc-search-result-item",onClick:()=>n(e)},e.thumbnail&&(0,r.createElement)("div",{className:"wpbc-search-result-thumbnail"},(0,r.createElement)("img",{src:e.thumbnail,alt:e.title,width:"60",height:"60"})),(0,r.createElement)("div",{className:"wpbc-search-result-content"},(0,r.createElement)("div",{className:"wpbc-search-result-title"},e.title),(0,r.createElement)("div",{className:"wpbc-search-result-meta"},(0,r.createElement)("span",{className:"wpbc-search-result-type"},"post"===e.type?(0,o.__)("投稿","wpbc"):(0,o.__)("固定ページ","wpbc")),(0,r.createElement)("span",{className:"wpbc-search-result-date"},e.date)),e.excerpt&&(0,r.createElement)("div",{className:"wpbc-search-result-excerpt"},e.excerpt))))):(0,r.createElement)("div",{className:"wpbc-search-no-results"},(0,o.__)("検索結果が見つかりませんでした","wpbc")))}function b({value:e,onChange:t,onKeyDown:n,onSelectResult:l,placeholder:a=(0,o.__)("https://example.com or キーワード","wpbc"),label:s=(0,o.__)("URLまたはサイト内検索","wpbc"),showSearch:m=!1,disabled:b=!1}){const h=u(e),[w,g]=(0,c.useState)([]),[_,E]=(0,c.useState)(!1),[f,v]=(0,c.useState)(!1),[y,x]=(0,c.useState)(""),C=(0,c.useRef)(null),k=(0,c.useCallback)(async e=>{if(!e||e.length<2)return g([]),v(!1),void x("");C.current&&C.current.abort(),C.current=new AbortController,E(!0),x("");try{const t=await i()({path:`/wpbc/v1/search?q=${encodeURIComponent(e)}`,method:"GET",signal:C.current.signal});t&&t.success?(g(t.data||[]),v(!0),x("")):(g([]),v(!1),x((0,o.__)("検索に失敗しました。","wpbc")))}catch(e){if("AbortError"===e.name)return;g([]),v(!1),x((0,o.__)("検索中にエラーが発生しました。","wpbc"))}finally{E(!1)}},[]);return(0,c.useEffect)(()=>{if(m&&!b){if(!e||""===e.trim())return g([]),v(!1),void x("");if(!h){const t=setTimeout(()=>{k(e)},300);return()=>{clearTimeout(t),C.current&&C.current.abort()}}g([]),v(!1),x("")}},[e,m,b,h,k]),(0,c.useEffect)(()=>{h&&(g([]),v(!1),E(!1),x(""))},[h]),(0,c.useEffect)(()=>()=>{C.current&&C.current.abort()},[]),(0,r.createElement)("div",{style:{position:"relative"}},(0,r.createElement)(p.TextControl,{label:s,value:e,onChange:t,onKeyDown:t=>{"Enter"===t.key&&(t.preventDefault(),u(e)&&n&&n(t))},placeholder:a,__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0}),m&&!b&&e&&""!==e.trim()&&!u(e)&&(f||y)&&(0,r.createElement)(d,{results:w,isLoading:_,onSelect:e=>{t(e.url),l&&l(e),v(!1),g([])},onClose:()=>{v(!1),g([])},error:y}))}function h({attributes:e}){const{url:t,title:n,description:l,thumbnailUrl:a,thumbnailId:c,showThumbnail:o,favicon:s,target:i="_blank",noopener:p=!0,nofollow:u=!1,noreferrer:m=!1,sponsored:d=!1,ugc:b=!1,isSelected:h=!1}=e,w=[];p&&w.push("noopener"),m&&w.push("noreferrer"),u&&w.push("nofollow"),d&&w.push("sponsored"),b&&w.push("ugc");const g=w.length>0?w.join(" "):"",_=a,E=o&&_;return(0,r.createElement)("article",{className:"wp-blogcard",cite:t},(0,r.createElement)("a",{href:t,target:i||void 0,rel:g||void 0,className:"wp-blogcard-item"},E&&(0,r.createElement)("figure",{className:"wp-blogcard-figure"},(0,r.createElement)("img",{src:_,alt:"","aria-hidden":"true"})),(0,r.createElement)("div",{className:"wp-blogcard-content"},(0,r.createElement)("div",{className:"wp-blogcard-title"},n),(0,r.createElement)("div",{className:"wp-blogcard-description"},l),(0,r.createElement)("div",{className:"wp-blogcard-cite"},s&&(0,r.createElement)("img",{className:"wp-blogcard-favicon",src:s,alt:"","aria-hidden":"true"}),(0,r.createElement)("div",{className:"wp-blogcard-domain"},function(e){try{return new URL(e).hostname}catch{return""}}(t))))))}function w({url:e,cached:t,onOpenLink:n}){return(0,r.createElement)("div",{className:"blogcard-preview-footer",style:{marginTop:"10px",display:"flex",justifyContent:"space-between",alignItems:"center"}},(0,r.createElement)("div",{style:{fontSize:"12px",color:"#666",display:"flex",alignItems:"center",gap:"4px"}},t&&(0,r.createElement)("span",{style:{padding:"2px 6px",borderRadius:"3px",fontSize:"10px",fontWeight:"bold"}},"CACHED")),(0,r.createElement)(p.Button,{type:"button",className:"wpbc-show-link-button",onClick:()=>n(e),disabled:!e,variant:"secondary",size:"small"},(0,o.__)("リンク先を表示","wpbc")))}function g({error:e,errorCode:t}){return e?(0,r.createElement)("div",{style:{color:"#cc0000",fontSize:"14px",margin:"8px 0",padding:"8px 0"}},e,t&&(0,r.createElement)("span",{style:{marginLeft:"8px",color:"#666",fontSize:"12px"}},"(",t,")")):null}function _({message:e=(0,o.__)("メタデータを取得中...","wpbc")}){return(0,r.createElement)("div",{style:{textAlign:"center",padding:"1.25rem"}},(0,r.createElement)(p.Spinner,null),(0,r.createElement)("p",{style:{fontSize:"0.875rem"}},e))}function E({attributes:e,setAttributes:t,inputUrl:n,setInputUrl:l,handleKeyDown:c}){const{target:s,noopener:i,nofollow:u,noreferrer:m,sponsored:d,ugc:b,showThumbnail:h,title:w,description:g,thumbnailUrl:_,thumbnailId:E}=e,v=()=>E&&_?(0,r.createElement)("div",{style:{width:"100%",aspectRatio:"16/9",overflow:"hidden",borderRadius:"4px",backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center"}},(0,r.createElement)("img",{src:_,alt:"",style:{width:"100%",height:"100%",objectFit:"cover"}})):(0,r.createElement)("div",{style:{backgroundColor:"#f0f0f0",borderRadius:"4px",display:"flex",alignItems:"center",justifyContent:"center",color:"#666",fontSize:"14px"}},(0,o.__)("サムネイルを設定","wpbc"));return(0,r.createElement)(a.InspectorControls,null,(0,r.createElement)(p.PanelBody,{title:(0,o.__)("ブロック設定","wpbc"),initialOpen:!0},(0,r.createElement)(p.TextControl,{label:(0,o.__)("URL","wpbc"),value:n,onChange:e=>l(e),onKeyDown:c,placeholder:(0,o.__)("https://example.com or キーワード","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("TARGET属性","wpbc")),(0,r.createElement)(p.SelectControl,{label:(0,o.__)("TARGET属性","wpbc"),value:s,onChange:e=>{t("_blank"===e?{target:e,noopener:!0}:{target:e})},options:[{label:(0,o.__)("なし","wpbc"),value:""},{label:(0,o.__)("_blank(別タブ)","wpbc"),value:"_blank"},{label:(0,o.__)("_self (同じタブ)","wpbc"),value:"_self"}]}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("Rel属性","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("noopener を追加","wpbc"),checked:i,disabled:"_blank"===s,onChange:e=>t({noopener:e})}),"_blank"===s&&(0,r.createElement)("p",{style:{fontSize:"12px",color:"#666",margin:"8px 0"}},(0,o.__)("_blankの場合はセキュリティ上noopenerが必須です","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=nofollow を追加","wpbc"),checked:u,onChange:e=>t({nofollow:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=noreferrer を追加","wpbc"),checked:m,onChange:e=>t({noreferrer:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=sponsored を追加","wpbc"),checked:d,onChange:e=>t({sponsored:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=ugc を追加","wpbc"),checked:b,onChange:e=>t({ugc:e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("サムネイル","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("サムネイルを表示しない","wpbc"),checked:!h,onChange:e=>t({showThumbnail:!e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("タイトルを手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("タイトル","wpbc"),value:w,onChange:e=>t({title:e}),placeholder:(0,o.__)("タイトルを入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("説明文を手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("説明文","wpbc"),value:g,onChange:e=>t({description:e}),placeholder:(0,o.__)("説明文を入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)(p.BaseControl,{label:(0,o.__)("サムネイルを手動で設定","wpbc")},(0,r.createElement)(a.MediaUploadCheck,null,(0,r.createElement)(a.MediaUpload,{onSelect:e=>{t({thumbnailId:e.id,thumbnailUrl:e.url,showThumbnail:!0})},allowedTypes:["image"],value:E,render:({open:e})=>(0,r.createElement)(p.Button,{onClick:e,className:"editor-post-featured-image__toggle"},(0,r.createElement)(v,null))})),(0,r.createElement)(p.Button,{style:{marginTop:"0.5rem"},className:"is-tertiary",onClick:()=>{t({thumbnailId:0,thumbnailUrl:"",showThumbnail:!1})}},(0,o.__)("クリア","wpbc"))),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("キャッシュ管理","wpbc")),(0,r.createElement)("div",{style:{display:"flex",gap:"10px",flexWrap:"wrap"}},(0,r.createElement)(p.Button,{secondary:!0,onClick:f("plugin"),style:{fontSize:"12px"}},(0,o.__)("ブログカードキャッシュクリア","wpbc")),(0,r.createElement)(p.Button,{isDestructive:!0,onClick:()=>{confirm((0,o.__)("全てのWordPressキャッシュをクリアしますか?","wpbc"))&&f("all")()},style:{fontSize:"12px"}},(0,o.__)("全キャッシュクリア","wpbc")))))}function f(e="plugin"){return async()=>{try{const t=await i()({path:"/wpbc/v1/clear-cache",method:"POST",data:{type:e}});t.success?alert(t.message):alert((0,o.__)("キャッシュのクリアに失敗しました。","wpbc"))}catch(e){console.error("Cache clear error:",e),alert((0,o.__)("キャッシュのクリア中にエラーが発生しました。","wpbc"))}}}(0,l.registerBlockType)("su/blogcard",{edit:function({attributes:e,setAttributes:t}){const{url:n,showThumbnail:l,title:s,description:p,thumbnailUrl:d,favicon:f}=e,[v,y]=(0,c.useState)(n||""),[x,C]=(0,c.useState)(!1),[k,T]=(0,c.useState)(""),[N,S]=(0,c.useState)(""),[R,U]=(0,c.useState)(!1),O=(0,a.useBlockProps)({className:"wp-block-blogcard"}),I=O.className?.includes("is-selected")||!1,D=e=>{"Enter"===e.key&&(e.preventDefault(),u(v)&&(v!==n&&t({url:"",title:"",description:"",thumbnailUrl:"",showThumbnail:!1,favicon:""}),(async()=>{if(!v||v.trim().length<10||!u(v))return T(""),void S("");C(!0),T(""),S("");try{const e=m(v)?`/wpbc/v1/internal-metadata?url=${encodeURIComponent(v)}`:"/wpbc/v1/metadata",n=await i()({path:e,method:m(v)?"GET":"POST",data:m(v)?void 0:{url:v}});if(n.success){const e=n.data;t({url:v,title:e.title||v,description:e.description||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""}),U(!0===e.cached),T(""),S("")}else{const e=n.message||n.data?.message||(0,o.__)("メタデータの取得に失敗しました。","wpbc"),t=n.code||n.data?.code||"UNKNOWN_ERROR";T(e),S(t)}}catch(e){const t=e.status||e.code||"NETWORK_ERROR";let n=e.message||(0,o.__)("メタデータの取得中にエラーが発生しました。","wpbc");e.data&&e.data.message&&(n=e.data.message),T(n),S(t)}finally{C(!1)}})()))},z=e=>{y(e.url),T(""),S(""),U(!1),t({url:e.url,title:e.title||e.url,description:e.excerpt||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""})};return(0,r.createElement)("div",{...O},(0,r.createElement)(E,{attributes:e,setAttributes:t,inputUrl:v,setInputUrl:y,handleKeyDown:D}),n||k?(0,r.createElement)("div",null,x&&(0,r.createElement)(_,null),I&&(0,r.createElement)(b,{key:"selected-search-input",value:v,onChange:e=>y(e),onKeyDown:D,onSelectResult:z,showSearch:!0,disabled:!1}),(0,r.createElement)(g,{error:k,errorCode:N}),!x&&!k&&n&&(0,r.createElement)("div",{className:"blogcard-preview"},(0,r.createElement)(h,{attributes:{url:n,title:s,description:p,thumbnailUrl:d,showThumbnail:l,favicon:f,isSelected:I}}),I&&(0,r.createElement)(w,{url:n,cached:R,onOpenLink:e=>{window.open(e,"_blank")}}))):(0,r.createElement)(b,{key:"placeholder-search-input",value:v,onChange:e=>y(e),onKeyDown:D,onSelectResult:z,showSearch:!0,disabled:!1}))},save:function({attributes:e}){const t=a.useBlockProps.save();return(0,r.createElement)("div",{...t},(0,r.createElement)(h,{attributes:e}))}})}},n={};function l(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,l),a.exports}l.m=t,e=[],l.O=(t,n,r,a)=>{if(!n){var c=1/0;for(p=0;p<e.length;p++){for(var[n,r,a]=e[p],o=!0,s=0;s<n.length;s++)(!1&a||c>=a)&&Object.keys(l.O).every(e=>l.O[e](n[s]))?n.splice(s--,1):(o=!1,a<c&&(c=a));if(o){e.splice(p--,1);var i=r();void 0!==i&&(t=i)}}return t}a=a||0;for(var p=e.length;p>0&&e[p-1][2]>a;p--)e[p]=e[p-1];e[p]=[n,r,a]},l.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return l.d(t,{a:t}),t},l.d=(e,t)=>{for(var n in t)l.o(t,n)&&!l.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var r,a,[c,o,s]=n,i=0;if(c.some(t=>0!==e[t])){for(r in o)l.o(o,r)&&(l.m[r]=o[r]);if(s)var p=s(l)}for(t&&t(n);i<c.length;i++)a=c[i],l.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return l.O(p)},n=globalThis.webpackChunkblogcard_for_wp=globalThis.webpackChunkblogcard_for_wp||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var r=l.O(void 0,[350],()=>l(486));r=l.O(r)})(); -
blogcard-for-wp/tags/2.0.6/build/style-index-rtl.css
r3357123 r3418932 1 :where(.wp-blo gcard){container-type:inline-size;margin-block:1.5rem}.wp-blogcard-item{--color-dark-gray:#18181b;--color-gray:#52525c;--color-light-gray:#e4e4e7;--color-base:#f4f4f5;--color-background:#fff;--border-radius:0.5rem;--padding:0.875rem;--gap:1rem;--image-width:5rem;--image-height:5rem;--bg-color:pink;background-color:var(--bg-color);background-color:var(--color-background);border:1px solid var(--color-light-gray);border-radius:var(--border-radius);color:var(--color-dark-gray);display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none;transition:background-color .2s ease;&:hover{background-color:var(--color-base);color:var(--color-dark-gray)}}.dark .wp-blogcard-item{--color-background:#18181b;--color-dark-gray:#f4f4f5;--color-gray:#9f9fa9;--color-light-gray:#52525c;--color-base:#27272a}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:.5rem;height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;margin:0;overflow:hidden;-webkit-line-clamp:2;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-gray);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-left:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-gray);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard-item{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem;--bg-color:orange;--border-radius:0.75rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}}1 :where(.wp-block-su-blogcard){margin-block:1.5rem}.wp-block-su-blogcard{container-type:inline-size;--color-text:#18181b;--color-text-light:#71717b;--color-ui:#e4e4e7;--color-surface:#f4f4f5;--color-background:#fff;--border-radius:0.125rem;--block-padding:1rem 1.25rem;--gap:1rem;--image-width:5rem;--image-height:5rem}.dark .wp-block-su-blogcard{--color-text:#f4f4f5;--color-text-light:#9f9fa9;--color-ui:#27272a;--color-surface:#18181b;--color-background:#09090b;--color-white:#09090b}.wp-blogcard{background-color:var(--color-background);border:1px solid var(--color-ui);border-radius:var(--border-radius);color:var(--color-text);transition:background-color .2s ease;&:hover{background-color:var(--color-surface);color:var(--color-text)}}.wp-blogcard-item{display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none!important}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:var(--border-radius);height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{color:var(--color-text);font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;-webkit-line-clamp:2;margin:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-text-light);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-left:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-text-light);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}} -
blogcard-for-wp/tags/2.0.6/build/style-index.css
r3357123 r3418932 1 :where(.wp-blo gcard){container-type:inline-size;margin-block:1.5rem}.wp-blogcard-item{--color-dark-gray:#18181b;--color-gray:#52525c;--color-light-gray:#e4e4e7;--color-base:#f4f4f5;--color-background:#fff;--border-radius:0.5rem;--padding:0.875rem;--gap:1rem;--image-width:5rem;--image-height:5rem;--bg-color:pink;background-color:var(--bg-color);background-color:var(--color-background);border:1px solid var(--color-light-gray);border-radius:var(--border-radius);color:var(--color-dark-gray);display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none;transition:background-color .2s ease;&:hover{background-color:var(--color-base);color:var(--color-dark-gray)}}.dark .wp-blogcard-item{--color-background:#18181b;--color-dark-gray:#f4f4f5;--color-gray:#9f9fa9;--color-light-gray:#52525c;--color-base:#27272a}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:.5rem;height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;margin:0;overflow:hidden;-webkit-line-clamp:2;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-gray);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-right:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-gray);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard-item{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem;--bg-color:orange;--border-radius:0.75rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}}1 :where(.wp-block-su-blogcard){margin-block:1.5rem}.wp-block-su-blogcard{container-type:inline-size;--color-text:#18181b;--color-text-light:#71717b;--color-ui:#e4e4e7;--color-surface:#f4f4f5;--color-background:#fff;--border-radius:0.125rem;--block-padding:1rem 1.25rem;--gap:1rem;--image-width:5rem;--image-height:5rem}.dark .wp-block-su-blogcard{--color-text:#f4f4f5;--color-text-light:#9f9fa9;--color-ui:#27272a;--color-surface:#18181b;--color-background:#09090b;--color-white:#09090b}.wp-blogcard{background-color:var(--color-background);border:1px solid var(--color-ui);border-radius:var(--border-radius);color:var(--color-text);transition:background-color .2s ease;&:hover{background-color:var(--color-surface);color:var(--color-text)}}.wp-blogcard-item{display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none!important}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:var(--border-radius);height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{color:var(--color-text);font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;-webkit-line-clamp:2;margin:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-text-light);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-right:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-text-light);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}} -
blogcard-for-wp/tags/2.0.6/readme.txt
r3368864 r3418932 5 5 Tested up to: 6.8.2 6 6 Requires PHP: 7.4 7 Stable tag: 2.0. 27 Stable tag: 2.0.6 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 98 98 == Changelog == 99 99 100 = 2.0.4 = 101 * Update CSS. 102 100 103 = 2.0.0 = 101 104 * Significantly improved block editor UI -
blogcard-for-wp/trunk/blogcard-for-wp.php
r3368864 r3418932 2 2 3 3 /** 4 * Plugin Name: Blogcard for WP4 * Plugin Name: SU Blocks - Blogcard 5 5 * Plugin URI: https://wordpress.org/plugins/blogcard-for-wp/ 6 6 * Description: URLを入力してブログカードを生成するブロックプラグイン 7 * Version: 2.0. 27 * Version: 2.0.6 8 8 * Author: Takashi Fujisaki 9 9 * License: GPL v2 or later … … 12 12 13 13 // 直接アクセスを防ぐ 14 if ( !defined('ABSPATH')) {15 exit;14 if ( ! defined( 'ABSPATH' ) ) { 15 exit; 16 16 } 17 17 18 18 // プラグインの定数 19 define('WPBC_PLUGIN_PATH', plugin_dir_path(__FILE__)); 19 define( 'WPBC_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); 20 21 /** 22 * カスタムブロックカテゴリーを追加 23 */ 24 function wpbc_block_categories( $categories, $editor_context ) { 25 if ( ! empty( $editor_context->post ) ) { 26 // 既存のカテゴリーが存在するかチェック 27 $exists = wp_list_pluck( $categories, 'slug' ); 28 if ( ! in_array( 'su-blocks', $exists, true ) ) { 29 array_push( 30 $categories, 31 array( 32 'slug' => 'su-blocks', 33 'title' => 'SU Blocks', 34 ) 35 ); 36 } 37 } 38 return $categories; 39 } 40 add_filter( 'block_categories_all', 'wpbc_block_categories', 10, 2 ); 20 41 21 42 /** … … 23 44 */ 24 45 function wpbc_init() { 25 // テキストドメインの読み込み26 load_plugin_textdomain('wpbc', false, dirname(plugin_basename(__FILE__)) . '/languages');27 28 // ブロックの登録29 register_block_type(WPBC_PLUGIN_PATH . 'build/block.json');30 } 31 add_action( 'init', 'wpbc_init');46 // テキストドメインの読み込み 47 load_plugin_textdomain( 'wpbc', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); 48 49 // ブロックの登録 50 register_block_type( WPBC_PLUGIN_PATH . 'build/block.json' ); 51 } 52 add_action( 'init', 'wpbc_init' ); 32 53 33 54 /** … … 35 56 */ 36 57 function wpbc_rest_api_init() { 37 register_rest_route('wpbc/v1', '/metadata', [ 38 'methods' => 'POST', 39 'callback' => 'wpbc_get_metadata', 40 'permission_callback' => function () { 41 return current_user_can('edit_posts'); 42 }, 43 'args' => [ 44 'url' => [ 45 'required' => true, 46 'type' => 'string', 47 'sanitize_callback' => 'esc_url_raw', 48 ], 49 ], 50 ]); 51 52 register_rest_route('wpbc/v1', '/internal-metadata', [ 53 'methods' => 'GET', 54 'callback' => 'wpbc_get_internal_metadata', 55 'permission_callback' => function () { 56 return current_user_can('edit_posts'); 57 }, 58 'args' => [ 59 'url' => [ 60 'required' => true, 61 'type' => 'string', 62 'sanitize_callback' => 'esc_url_raw', 63 ], 64 ], 65 ]); 66 67 register_rest_route('wpbc/v1', '/search', [ 68 'methods' => 'GET', 69 'callback' => 'wpbc_search_posts', 70 'permission_callback' => function () { 71 return current_user_can('edit_posts'); 72 }, 73 'args' => [ 74 'q' => [ 75 'required' => true, 76 'type' => 'string', 77 'sanitize_callback' => 'sanitize_text_field', 78 ], 79 ], 80 ]); 81 82 register_rest_route('wpbc/v1', '/clear-cache', [ 83 'methods' => 'POST', 84 'callback' => 'wpbc_clear_cache', 85 'permission_callback' => function () { 86 return current_user_can('edit_posts'); 87 }, 88 'args' => [ 89 'type' => [ 90 'type' => 'string', 91 'default' => 'plugin', 92 'enum' => ['plugin', 'all'] 93 ], 94 ], 95 ]); 96 } 97 add_action('rest_api_init', 'wpbc_rest_api_init'); 58 register_rest_route( 59 'wpbc/v1', 60 '/metadata', 61 array( 62 'methods' => 'POST', 63 'callback' => 'wpbc_get_metadata', 64 'permission_callback' => function () { 65 return current_user_can( 'edit_posts' ); 66 }, 67 'args' => array( 68 'url' => array( 69 'required' => true, 70 'type' => 'string', 71 'sanitize_callback' => 'esc_url_raw', 72 ), 73 ), 74 ) 75 ); 76 77 register_rest_route( 78 'wpbc/v1', 79 '/internal-metadata', 80 array( 81 'methods' => 'GET', 82 'callback' => 'wpbc_get_internal_metadata', 83 'permission_callback' => function () { 84 return current_user_can( 'edit_posts' ); 85 }, 86 'args' => array( 87 'url' => array( 88 'required' => true, 89 'type' => 'string', 90 'sanitize_callback' => 'esc_url_raw', 91 ), 92 ), 93 ) 94 ); 95 96 register_rest_route( 97 'wpbc/v1', 98 '/search', 99 array( 100 'methods' => 'GET', 101 'callback' => 'wpbc_search_posts', 102 'permission_callback' => function () { 103 return current_user_can( 'edit_posts' ); 104 }, 105 'args' => array( 106 'q' => array( 107 'required' => true, 108 'type' => 'string', 109 'sanitize_callback' => 'sanitize_text_field', 110 ), 111 ), 112 ) 113 ); 114 115 register_rest_route( 116 'wpbc/v1', 117 '/clear-cache', 118 array( 119 'methods' => 'POST', 120 'callback' => 'wpbc_clear_cache', 121 'permission_callback' => function () { 122 return current_user_can( 'edit_posts' ); 123 }, 124 'args' => array( 125 'type' => array( 126 'type' => 'string', 127 'default' => 'plugin', 128 'enum' => array( 'plugin', 'all' ), 129 ), 130 ), 131 ) 132 ); 133 } 134 add_action( 'rest_api_init', 'wpbc_rest_api_init' ); 98 135 99 136 /** 100 137 * メタデータ取得のREST APIコールバック 101 138 */ 102 function wpbc_get_metadata( $request) {103 $url = $request->get_param('url');104 105 if (empty($url)) {106 return new WP_Error('missing_url', 'URLが指定されていません。', ['status' => 400]);107 }108 109 $metadata = wpbc_fetch_metadata($url);110 111 // WP_Errorオブジェクトの場合はそのまま返す112 if (is_wp_error($metadata)) {113 return $metadata;114 }115 116 // falseの場合は一般的なエラー117 if ($metadata === false) {118 return new WP_Error('metadata_fetch_failed', 'メタデータの取得に失敗しました。', ['status' => 500]);119 }120 121 return [ 122 'success' => true,123 'data' => $metadata 124 ];139 function wpbc_get_metadata( $request ) { 140 $url = $request->get_param( 'url' ); 141 142 if ( empty( $url ) ) { 143 return new WP_Error( 'missing_url', 'URLが指定されていません。', array( 'status' => 400 ) ); 144 } 145 146 $metadata = wpbc_fetch_metadata( $url ); 147 148 // WP_Errorオブジェクトの場合はそのまま返す 149 if ( is_wp_error( $metadata ) ) { 150 return $metadata; 151 } 152 153 // falseの場合は一般的なエラー 154 if ( false === $metadata ) { 155 return new WP_Error( 'metadata_fetch_failed', 'メタデータの取得に失敗しました。', array( 'status' => 500 ) ); 156 } 157 158 return array( 159 'success' => true, 160 'data' => $metadata, 161 ); 125 162 } 126 163 … … 128 165 * 内部メタデータ取得のREST APIコールバック 129 166 */ 130 function wpbc_get_internal_metadata( $request) {131 $url = $request->get_param('url');132 133 if (empty($url)) {134 return new WP_Error('missing_url', 'URLが指定されていません。', ['status' => 400]);135 }136 137 $metadata = wpbc_fetch_internal_metadata($url);138 139 if (is_wp_error($metadata)) {140 return $metadata;141 }142 143 return [ 144 'success' => true,145 'data' => $metadata 146 ];167 function wpbc_get_internal_metadata( $request ) { 168 $url = $request->get_param( 'url' ); 169 170 if ( empty( $url ) ) { 171 return new WP_Error( 'missing_url', 'URLが指定されていません。', array( 'status' => 400 ) ); 172 } 173 174 $metadata = wpbc_fetch_internal_metadata( $url ); 175 176 if ( is_wp_error( $metadata ) ) { 177 return $metadata; 178 } 179 180 return array( 181 'success' => true, 182 'data' => $metadata, 183 ); 147 184 } 148 185 … … 150 187 * URLが内部サイトのURLかどうかを判定する 151 188 */ 152 function wpbc_is_internal_url( $url) {153 if (empty($url)) {154 return false;155 }156 157 $site_url= get_site_url();158 $parsed_site_url = parse_url($site_url);159 $parsed_url = parse_url($url);160 161 if (!$parsed_site_url || !$parsed_url) {162 return false;163 }164 165 $site_host = $parsed_site_url['host'];166 $url_host= $parsed_url['host'];167 168 // 完全一致169 if ($site_host === $url_host) {170 return true;171 }172 173 // サブドメインのチェック174 // 例:www.example.com は example.com のサブドメイン175 $site_suffix = '.' . $url_host;176 $url_suffix= '.' . $site_host;177 if (178 substr($site_host, -strlen($site_suffix)) === $site_suffix ||179 substr($url_host, -strlen($url_suffix)) === $url_suffix180 ) {181 return true;182 }183 184 return false;189 function wpbc_is_internal_url( $url ) { 190 if ( empty( $url ) ) { 191 return false; 192 } 193 194 $site_url = get_site_url(); 195 $parsed_site_url = parse_url( $site_url ); 196 $parsed_url = parse_url( $url ); 197 198 if ( ! $parsed_site_url || ! $parsed_url ) { 199 return false; 200 } 201 202 $site_host = $parsed_site_url['host']; 203 $url_host = $parsed_url['host']; 204 205 // 完全一致 206 if ( $site_host === $url_host ) { 207 return true; 208 } 209 210 // サブドメインのチェック 211 // 例:www.example.com は example.com のサブドメイン 212 $site_suffix = '.' . $url_host; 213 $url_suffix = '.' . $site_host; 214 if ( 215 substr( $site_host, -strlen( $site_suffix ) ) === $site_suffix || 216 substr( $url_host, -strlen( $url_suffix ) ) === $url_suffix 217 ) { 218 return true; 219 } 220 221 return false; 185 222 } 186 223 … … 188 225 * メタデータを取得する関数 189 226 */ 190 function wpbc_fetch_metadata($url) { 191 // キャッシュキーを生成 192 $cache_key = 'wpbc_metadata_' . md5($url); 193 194 // キャッシュから取得を試行 195 $cached_data = get_transient($cache_key); 196 if ($cached_data !== false) { 197 // キャッシュから取得した場合はcachedフラグを追加 198 $cached_data['cached'] = true; 199 return $cached_data; 200 } 201 202 // URLの検証 203 if (!filter_var($url, FILTER_VALIDATE_URL)) { 204 return new WP_Error('invalid_url', '無効なURL形式です。', ['status' => 400]); 205 } 206 207 // 内部URLの場合は内部メタデータ取得関数を使用 208 if (wpbc_is_internal_url($url)) { 209 $metadata = wpbc_fetch_internal_metadata($url); 210 if (is_wp_error($metadata)) { 211 return $metadata; 212 } 213 214 // キャッシュに保存(24時間) 215 set_transient($cache_key, $metadata, 24 * HOUR_IN_SECONDS); 216 217 return $metadata; 218 } 219 220 $metadata = [ 221 'title' => '', 222 'description' => '', 223 'thumbnail' => '', 224 'domain' => parse_url($url, PHP_URL_HOST), 225 'final_url' => $url // リダイレクト後の最終URL 226 ]; 227 228 // HTTPリクエストでメタデータを取得(リダイレクトを追跡) 229 $response = wp_remote_get($url, [ 230 'timeout' => 20, // タイムアウトを20秒に延長 231 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 232 'redirection' => 5, // 最大5回までリダイレクトを追跡 233 'sslverify' => false, // SSL証明書の検証を無効化(テスト用) 234 'headers' => [ 235 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 236 'Accept-Language' => 'ja-JP,ja;q=0.9,en;q=0.8', 237 'Accept-Encoding' => 'gzip, deflate, br', 238 'Connection' => 'keep-alive', 239 'Upgrade-Insecure-Requests' => '1', 240 'Sec-Fetch-Dest' => 'document', 241 'Sec-Fetch-Mode' => 'navigate', 242 'Sec-Fetch-Site' => 'none', 243 'Cache-Control' => 'no-cache', 244 ] 245 ]); 246 247 // ネットワークエラーの場合 248 if (is_wp_error($response)) { 249 $error_message = $response->get_error_message(); 250 $error_code = 'network_error'; 251 $status_code = 500; 252 253 // タイムアウトエラーの場合は特別な処理 254 if (strpos($error_message, 'timeout') !== false || strpos($error_message, 'timed out') !== false) { 255 $error_code = 'timeout_error'; 256 $error_message = 'サーバーの応答が遅すぎます。しばらく時間をおいてから再度お試しください。'; 257 $status_code = 408; // Request Timeout 258 } elseif (strpos($error_message, 'Connection refused') !== false) { 259 $error_code = 'connection_refused'; 260 $error_message = 'サーバーに接続できませんでした。'; 261 $status_code = 503; // Service Unavailable 262 } elseif (strpos($error_message, 'SSL') !== false || strpos($error_message, 'certificate') !== false) { 263 $error_code = 'ssl_error'; 264 $error_message = 'SSL証明書の検証に失敗しました。'; 265 $status_code = 495; // SSL Certificate Error 266 } 267 268 // デバッグ用ログ 269 error_log('wpbc_fetch_metadata network error for URL: ' . $url . ' - Error: ' . $error_message . ' - Code: ' . $error_code); 270 return new WP_Error($error_code, $error_message, ['status' => $status_code]); 271 } 272 273 // HTTPステータスコードを取得 274 $response_code = wp_remote_retrieve_response_code($response); 275 276 // 200以外の場合はエラーを返す 277 if ($response_code !== 200) { 278 $error_message = 'HTTPエラーが発生しました'; 279 switch ($response_code) { 280 case 404: 281 $error_message = 'ページが見つかりません (404)'; 282 break; 283 case 403: 284 $error_message = 'アクセスが拒否されました (403)'; 285 break; 286 case 500: 287 $error_message = 'サーバーエラーが発生しました (500)'; 288 break; 289 case 503: 290 $error_message = 'サービスが利用できません (503)'; 291 break; 292 default: 293 $error_message = "HTTPエラーが発生しました ({$response_code})"; 294 break; 295 } 296 // デバッグ用ログ 297 error_log('wpbc_fetch_metadata HTTP error for URL: ' . $url . ' - Status: ' . $response_code . ' - Message: ' . $error_message); 298 return new WP_Error('http_error', $error_message, ['status' => $response_code]); 299 } 300 301 if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) { 302 $body = wp_remote_retrieve_body($response); 303 304 // リダイレクト後の最終URLを取得 305 // wp_remote_get()は自動的にリダイレクトを追跡するので、 306 // レスポンスのURLを取得する 307 $response_url = wp_remote_retrieve_header($response, 'url'); 308 if ($response_url) { 309 $final_url = $response_url; 310 } else { 311 // ヘッダーから取得できない場合は元のURLを使用 312 $final_url = $url; 313 } 314 315 // 最終URLのドメインとURLを更新 316 $metadata['domain'] = parse_url($final_url, PHP_URL_HOST); 317 $metadata['final_url'] = $final_url; 318 319 // HTMLをパースしてメタデータを抽出 320 $dom = new DOMDocument(); 321 @$dom->loadHTML(mb_convert_encoding($body, 'HTML-ENTITIES', 'UTF-8')); 322 $xpath = new DOMXPath($dom); 323 324 // タイトルを取得 325 $title_nodes = $xpath->query('//title'); 326 if ($title_nodes->length > 0) { 327 $metadata['title'] = trim($title_nodes->item(0)->textContent); 328 } 329 330 // OGPメタタグを取得 331 $og_title = $xpath->query('//meta[@property="og:title"]/@content'); 332 if ($og_title->length > 0) { 333 $metadata['title'] = trim($og_title->item(0)->textContent); 334 } 335 336 // 説明文を取得 337 $description_nodes = $xpath->query('//meta[@name="description"]/@content'); 338 if ($description_nodes->length > 0) { 339 $metadata['description'] = trim($description_nodes->item(0)->textContent); 340 } 341 342 $og_description = $xpath->query('//meta[@property="og:description"]/@content'); 343 if ($og_description->length > 0) { 344 $metadata['description'] = trim($og_description->item(0)->textContent); 345 } 346 347 // サムネイル画像を取得 348 $og_image = $xpath->query('//meta[@property="og:image"]/@content'); 349 if ($og_image->length > 0) { 350 $metadata['thumbnail'] = trim($og_image->item(0)->textContent); 351 } 352 353 // ファビコンを取得 354 $favicon_url = wpbc_get_favicon_url($xpath, $url); 355 if ($favicon_url) { 356 $metadata['favicon'] = $favicon_url; 357 } 358 } 359 360 // 新しく取得した場合はcachedフラグをfalseに設定 361 $metadata['cached'] = false; 362 363 // キャッシュに保存(24時間) 364 set_transient($cache_key, $metadata, 24 * HOUR_IN_SECONDS); 365 366 return $metadata; 227 function wpbc_fetch_metadata( $url ) { 228 // キャッシュキーを生成 229 $cache_key = 'wpbc_metadata_' . md5( $url ); 230 231 // キャッシュから取得を試行 232 $cached_data = get_transient( $cache_key ); 233 if ( false !== $cached_data ) { 234 // キャッシュから取得した場合はcachedフラグを追加 235 $cached_data['cached'] = true; 236 return $cached_data; 237 } 238 239 // URLの検証 240 if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { 241 return new WP_Error( 'invalid_url', '無効なURL形式です。', array( 'status' => 400 ) ); 242 } 243 244 // 内部URLの場合は内部メタデータ取得関数を使用 245 if ( wpbc_is_internal_url( $url ) ) { 246 $metadata = wpbc_fetch_internal_metadata( $url ); 247 if ( is_wp_error( $metadata ) ) { 248 return $metadata; 249 } 250 251 // キャッシュに保存(24時間) 252 set_transient( $cache_key, $metadata, 24 * HOUR_IN_SECONDS ); 253 254 return $metadata; 255 } 256 257 $metadata = array( 258 'title' => '', 259 'description' => '', 260 'thumbnail' => '', 261 'domain' => parse_url( $url, PHP_URL_HOST ), 262 'final_url' => $url, // リダイレクト後の最終URL 263 ); 264 265 // HTTPリクエストでメタデータを取得(リダイレクトを追跡) 266 $response = wp_remote_get( 267 $url, 268 array( 269 'timeout' => 20, // タイムアウトを20秒に延長 270 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 271 'redirection' => 5, // 最大5回までリダイレクトを追跡 272 'sslverify' => false, // SSL証明書の検証を無効化(テスト用) 273 'headers' => array( 274 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 275 'Accept-Language' => 'ja-JP,ja;q=0.9,en;q=0.8', 276 'Accept-Encoding' => 'gzip, deflate, br', 277 'Connection' => 'keep-alive', 278 'Upgrade-Insecure-Requests' => '1', 279 'Sec-Fetch-Dest' => 'document', 280 'Sec-Fetch-Mode' => 'navigate', 281 'Sec-Fetch-Site' => 'none', 282 'Cache-Control' => 'no-cache', 283 ), 284 ) 285 ); 286 287 // ネットワークエラーの場合 288 if ( is_wp_error( $response ) ) { 289 $error_message = $response->get_error_message(); 290 $error_code = 'network_error'; 291 $status_code = 500; 292 293 // タイムアウトエラーの場合は特別な処理 294 if ( false !== strpos( $error_message, 'timeout' ) || false !== strpos( $error_message, 'timed out' ) ) { 295 $error_code = 'timeout_error'; 296 $error_message = 'サーバーの応答が遅すぎます。しばらく時間をおいてから再度お試しください。'; 297 $status_code = 408; // Request Timeout 298 } elseif ( false !== strpos( $error_message, 'Connection refused' ) ) { 299 $error_code = 'connection_refused'; 300 $error_message = 'サーバーに接続できませんでした。'; 301 $status_code = 503; // Service Unavailable 302 } elseif ( false !== strpos( $error_message, 'SSL' ) || false !== strpos( $error_message, 'certificate' ) ) { 303 $error_code = 'ssl_error'; 304 $error_message = 'SSL証明書の検証に失敗しました。'; 305 $status_code = 495; // SSL Certificate Error 306 } 307 308 // デバッグ用ログ 309 error_log( 'wpbc_fetch_metadata network error for URL: ' . $url . ' - Error: ' . $error_message . ' - Code: ' . $error_code ); 310 return new WP_Error( $error_code, $error_message, array( 'status' => $status_code ) ); 311 } 312 313 // HTTPステータスコードを取得 314 $response_code = wp_remote_retrieve_response_code( $response ); 315 316 // 200以外の場合はエラーを返す 317 if ( 200 !== $response_code ) { 318 $error_message = 'HTTPエラーが発生しました'; 319 switch ( $response_code ) { 320 case 404: 321 $error_message = 'ページが見つかりません (404)'; 322 break; 323 case 403: 324 $error_message = 'アクセスが拒否されました (403)'; 325 break; 326 case 500: 327 $error_message = 'サーバーエラーが発生しました (500)'; 328 break; 329 case 503: 330 $error_message = 'サービスが利用できません (503)'; 331 break; 332 default: 333 $error_message = "HTTPエラーが発生しました ({$response_code})"; 334 break; 335 } 336 // デバッグ用ログ 337 error_log( 'wpbc_fetch_metadata HTTP error for URL: ' . $url . ' - Status: ' . $response_code . ' - Message: ' . $error_message ); 338 return new WP_Error( 'http_error', $error_message, array( 'status' => $response_code ) ); 339 } 340 341 if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { 342 $body = wp_remote_retrieve_body( $response ); 343 344 // リダイレクト後の最終URLを取得 345 // wp_remote_get()は自動的にリダイレクトを追跡するので、 346 // レスポンスのURLを取得する 347 $response_url = wp_remote_retrieve_header( $response, 'url' ); 348 if ( $response_url ) { 349 $final_url = $response_url; 350 } else { 351 // ヘッダーから取得できない場合は元のURLを使用 352 $final_url = $url; 353 } 354 355 // 最終URLのドメインとURLを更新 356 $metadata['domain'] = parse_url( $final_url, PHP_URL_HOST ); 357 $metadata['final_url'] = $final_url; 358 359 // HTMLをパースしてメタデータを抽出 360 $dom = new DOMDocument(); 361 $libxml_previous_state = libxml_use_internal_errors( true ); 362 $dom->loadHTML( mb_convert_encoding( $body, 'HTML-ENTITIES', 'UTF-8' ) ); 363 libxml_clear_errors(); 364 libxml_use_internal_errors( $libxml_previous_state ); 365 $xpath = new DOMXPath( $dom ); 366 367 // タイトルを取得 368 $title_nodes = $xpath->query( '//title' ); 369 if ( $title_nodes->length > 0 ) { 370 $metadata['title'] = trim( $title_nodes->item( 0 )->textContent ); 371 } 372 373 // OGPメタタグを取得 374 $og_title = $xpath->query( '//meta[@property="og:title"]/@content' ); 375 if ( $og_title->length > 0 ) { 376 $metadata['title'] = trim( $og_title->item( 0 )->textContent ); 377 } 378 379 // 説明文を取得 380 $description_nodes = $xpath->query( '//meta[@name="description"]/@content' ); 381 if ( $description_nodes->length > 0 ) { 382 $metadata['description'] = trim( $description_nodes->item( 0 )->textContent ); 383 } 384 385 $og_description = $xpath->query( '//meta[@property="og:description"]/@content' ); 386 if ( $og_description->length > 0 ) { 387 $metadata['description'] = trim( $og_description->item( 0 )->textContent ); 388 } 389 390 // サムネイル画像を取得 391 $og_image = $xpath->query( '//meta[@property="og:image"]/@content' ); 392 if ( $og_image->length > 0 ) { 393 $metadata['thumbnail'] = trim( $og_image->item( 0 )->textContent ); 394 } 395 396 // ファビコンを取得 397 $favicon_url = wpbc_get_favicon_url( $xpath, $url ); 398 if ( $favicon_url ) { 399 $metadata['favicon'] = $favicon_url; 400 } 401 } 402 403 // 新しく取得した場合はcachedフラグをfalseに設定 404 $metadata['cached'] = false; 405 406 // キャッシュに保存(24時間) 407 set_transient( $cache_key, $metadata, 24 * HOUR_IN_SECONDS ); 408 409 return $metadata; 367 410 } 368 411 … … 370 413 * サイト内検索のREST APIコールバック 371 414 */ 372 function wpbc_search_posts( $request) {373 $query = $request->get_param('q');374 375 if (empty($query)) {376 return new WP_Error('missing_query', '検索クエリが指定されていません。', ['status' => 400]);377 }378 379 // 投稿と固定ページを検索380 $args = [ 381 'post_type' => ['post', 'page'],382 'post_status'=> 'publish',383 's'=> $query,384 'posts_per_page' => -1,385 'orderby'=> 'relevance',386 'order' => 'DESC' 387 ];388 389 $posts = get_posts($args);390 $results = [];391 392 // 投稿を先に並べる393 $posts_sorted = [];394 $pages_sorted = [];395 396 foreach ($posts as $post) {397 if (get_post_type($post->ID) === 'post') {398 $posts_sorted[] = $post;399 } else {400 $pages_sorted[] = $post;401 }402 }403 404 // 投稿と固定ページを結合(投稿を優先)405 $sorted_posts = array_merge($posts_sorted, $pages_sorted);406 407 foreach ($sorted_posts as $post) {408 $results[] = [ 409 'id'=> $post->ID,410 'title' => get_the_title($post->ID),411 'url' => get_permalink($post->ID),412 'type' => get_post_type($post->ID),413 'date' => get_the_date('Y-m-d', $post->ID),414 'thumbnail' => get_the_post_thumbnail_url($post->ID, 'thumbnail') 415 ];416 }417 418 return [ 419 'success' => true,420 'data' => $results 421 ];415 function wpbc_search_posts( $request ) { 416 $query = $request->get_param( 'q' ); 417 418 if ( empty( $query ) ) { 419 return new WP_Error( 'missing_query', '検索クエリが指定されていません。', array( 'status' => 400 ) ); 420 } 421 422 // 投稿と固定ページを検索 423 $args = array( 424 'post_type' => array( 'post', 'page' ), 425 'post_status' => 'publish', 426 's' => $query, 427 'posts_per_page' => -1, 428 'orderby' => 'relevance', 429 'order' => 'DESC', 430 ); 431 432 $posts = get_posts( $args ); 433 $results = array(); 434 435 // 投稿を先に並べる 436 $posts_sorted = array(); 437 $pages_sorted = array(); 438 439 foreach ( $posts as $post ) { 440 if ( 'post' === get_post_type( $post->ID ) ) { 441 $posts_sorted[] = $post; 442 } else { 443 $pages_sorted[] = $post; 444 } 445 } 446 447 // 投稿と固定ページを結合(投稿を優先) 448 $sorted_posts = array_merge( $posts_sorted, $pages_sorted ); 449 450 foreach ( $sorted_posts as $post ) { 451 $results[] = array( 452 'id' => $post->ID, 453 'title' => get_the_title( $post->ID ), 454 'url' => get_permalink( $post->ID ), 455 'type' => get_post_type( $post->ID ), 456 'date' => get_the_date( 'Y-m-d', $post->ID ), 457 'thumbnail' => get_the_post_thumbnail_url( $post->ID, 'thumbnail' ), 458 ); 459 } 460 461 return array( 462 'success' => true, 463 'data' => $results, 464 ); 422 465 } 423 466 … … 425 468 * 内部サイトのメタデータを取得する 426 469 */ 427 function wpbc_fetch_internal_metadata( $url) {428 // URLから投稿IDを取得429 $post_id = url_to_postid($url);430 431 if (!$post_id) {432 return new WP_Error('post_not_found', '指定されたURLの投稿が見つかりません。', ['status' => 404]);433 }434 435 // 投稿のタイトルを取得436 $title = get_the_title($post_id);437 438 // 投稿の抜粋を取得439 $excerpt = get_the_excerpt($post_id);440 441 // サムネイル画像を取得(thumbnailサイズ)442 $thumbnail_id = get_post_thumbnail_id($post_id);443 $thumbnail_url = '';444 if ($thumbnail_id) {445 $thumbnail_url = wp_get_attachment_image_url($thumbnail_id, 'thumbnail');446 }447 448 return [ 449 'title'=> $title,450 'description' => $excerpt,451 'thumbnail'=> $thumbnail_url,452 'url'=> $url,453 'cached' => false 454 ];470 function wpbc_fetch_internal_metadata( $url ) { 471 // URLから投稿IDを取得 472 $post_id = url_to_postid( $url ); 473 474 if ( ! $post_id ) { 475 return new WP_Error( 'post_not_found', '指定されたURLの投稿が見つかりません。', array( 'status' => 404 ) ); 476 } 477 478 // 投稿のタイトルを取得 479 $title = get_the_title( $post_id ); 480 481 // 投稿の抜粋を取得 482 $excerpt = get_the_excerpt( $post_id ); 483 484 // サムネイル画像を取得(thumbnailサイズ) 485 $thumbnail_id = get_post_thumbnail_id( $post_id ); 486 $thumbnail_url = ''; 487 if ( $thumbnail_id ) { 488 $thumbnail_url = wp_get_attachment_image_url( $thumbnail_id, 'thumbnail' ); 489 } 490 491 return array( 492 'title' => $title, 493 'description' => $excerpt, 494 'thumbnail' => $thumbnail_url, 495 'url' => $url, 496 'cached' => false, 497 ); 455 498 } 456 499 … … 458 501 * キャッシュクリアのREST APIコールバック 459 502 */ 460 function wpbc_clear_cache( $request) {461 global $wpdb;462 463 $type = $request->get_param('type');464 $cleared_count = 0;465 466 if ($type === 'all') {467 // 全てのWordPressキャッシュをクリア468 $result= $wpdb->query(469 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'"470 );471 $cleared_count = $result;472 473 // オブジェクトキャッシュもクリア474 if (function_exists('wp_cache_flush')) {475 wp_cache_flush();476 }477 478 $message = '全てのキャッシュをクリアしました。';479 } else {480 // プラグインのキャッシュのみをクリア481 $result= $wpdb->query(482 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wpbc_metadata_%' OR option_name LIKE '_transient_timeout_wpbc_metadata_%'"483 );484 $cleared_count = $result;485 486 $message = 'ブログカードのキャッシュをクリアしました。';487 }488 489 return [ 490 'success'=> true,491 'message'=> $message,492 'cleared_count' => $cleared_count 493 ];503 function wpbc_clear_cache( $request ) { 504 global $wpdb; 505 506 $type = $request->get_param( 'type' ); 507 $cleared_count = 0; 508 509 if ( 'all' === $type ) { 510 // 全てのWordPressキャッシュをクリア 511 $result = $wpdb->query( 512 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' OR option_name LIKE '_transient_timeout_%'" 513 ); 514 $cleared_count = $result; 515 516 // オブジェクトキャッシュもクリア 517 if ( function_exists( 'wp_cache_flush' ) ) { 518 wp_cache_flush(); 519 } 520 521 $message = '全てのキャッシュをクリアしました。'; 522 } else { 523 // プラグインのキャッシュのみをクリア 524 $result = $wpdb->query( 525 "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wpbc_metadata_%' OR option_name LIKE '_transient_timeout_wpbc_metadata_%'" 526 ); 527 $cleared_count = $result; 528 529 $message = 'ブログカードのキャッシュをクリアしました。'; 530 } 531 532 return array( 533 'success' => true, 534 'message' => $message, 535 'cleared_count' => $cleared_count, 536 ); 494 537 } 495 538 … … 497 540 * ファビコンを取得する関数 498 541 */ 499 function wpbc_get_favicon_url($xpath, $base_url) { 500 $favicon_url = null; 501 502 // ファビコンのURLを取得するクエリ(優先順位順) 503 $queries = [ 504 "//link[@rel='icon' and @type='image/svg+xml']", 505 "//link[@rel='icon' and @type='image/png']", 506 "//link[@rel='shortcut icon']", 507 "//link[contains(@rel, 'icon')]", 508 "//link[@rel='apple-touch-icon']", 509 "//meta[@name='msapplication-TileImage']" 510 ]; 511 512 foreach ($queries as $query) { 513 $results = $xpath->query($query); 514 foreach ($results as $item) { 515 if ($item instanceof DOMElement) { 516 $attr = ($item->tagName == 'meta') ? 'content' : 'href'; 517 $temp_url = $item->getAttribute($attr); 518 519 // URLを絶対URLに変換 520 $temp_url = wpbc_convert_to_absolute_url($temp_url, $base_url); 521 522 // ファビコンのURLが有効かチェック 523 if (wpbc_check_favicon_exists($temp_url)) { 524 return $temp_url; 525 } 526 } 527 } 528 } 529 530 // ドメイン直下のファビコンを探す 531 $default_favicons = [ 532 wpbc_convert_to_absolute_url("/favicon.svg", $base_url), 533 wpbc_convert_to_absolute_url("/favicon.ico", $base_url) 534 ]; 535 536 foreach ($default_favicons as $favicon_url) { 537 if (wpbc_check_favicon_exists($favicon_url)) { 538 return $favicon_url; 539 } 540 } 541 542 return null; 542 function wpbc_get_favicon_url( $xpath, $base_url ) { 543 $favicon_url = null; 544 545 // ファビコンのURLを取得するクエリ(優先順位順) 546 $queries = array( 547 "//link[@rel='icon' and @type='image/svg+xml']", 548 "//link[@rel='icon' and @type='image/png']", 549 "//link[@rel='shortcut icon']", 550 "//link[contains(@rel, 'icon')]", 551 "//link[@rel='apple-touch-icon']", 552 "//meta[@name='msapplication-TileImage']", 553 ); 554 555 foreach ( $queries as $query ) { 556 $results = $xpath->query( $query ); 557 foreach ( $results as $item ) { 558 if ( $item instanceof DOMElement ) { 559 $attr = $item->getAttribute( 'href' ); 560 if ( empty( $attr ) ) { 561 $attr = $item->getAttribute( 'content' ); 562 } 563 $temp_url = $attr; 564 565 // URLを絶対URLに変換 566 $temp_url = wpbc_convert_to_absolute_url( $temp_url, $base_url ); 567 568 // ファビコンのURLが有効かチェック 569 if ( wpbc_check_favicon_exists( $temp_url ) ) { 570 return $temp_url; 571 } 572 } 573 } 574 } 575 576 // ドメイン直下のファビコンを探す 577 $default_favicons = array( 578 wpbc_convert_to_absolute_url( '/favicon.svg', $base_url ), 579 wpbc_convert_to_absolute_url( '/favicon.ico', $base_url ), 580 ); 581 582 foreach ( $default_favicons as $favicon_url ) { 583 if ( wpbc_check_favicon_exists( $favicon_url ) ) { 584 return $favicon_url; 585 } 586 } 587 588 return null; 543 589 } 544 590 … … 546 592 * 相対URLを絶対URLに変換する 547 593 */ 548 function wpbc_convert_to_absolute_url( $url, $base_url) {549 if (preg_match('/^https?:\/\//', $url)) {550 return $url;551 }552 553 $parts = parse_url($base_url);554 $scheme = $parts['scheme'];555 $host= $parts['host'];556 557 if (strpos($url, '/') === 0) {558 return "$scheme://$host$url";559 }560 561 $base_path = isset($parts['path']) ? dirname($parts['path']) : '';562 return "$scheme://$host" . rtrim($base_path, '/') . '/' . ltrim($url, '/');594 function wpbc_convert_to_absolute_url( $url, $base_url ) { 595 if ( preg_match( '/^https?:\/\//', $url ) ) { 596 return $url; 597 } 598 599 $parts = parse_url( $base_url ); 600 $scheme = $parts['scheme']; 601 $host = $parts['host']; 602 603 if ( 0 === strpos( $url, '/' ) ) { 604 return "$scheme://$host$url"; 605 } 606 607 $base_path = isset( $parts['path'] ) ? dirname( $parts['path'] ) : ''; 608 return "$scheme://$host" . rtrim( $base_path, '/' ) . '/' . ltrim( $url, '/' ); 563 609 } 564 610 … … 566 612 * ファビコンが存在するかチェックする 567 613 */ 568 function wpbc_check_favicon_exists( $url) {569 $headers = @get_headers($url, 1);570 return $headers && strpos($headers[0], '200 OK') !== false;571 } 614 function wpbc_check_favicon_exists( $url ) { 615 $headers = get_headers( $url, 1 ); 616 return $headers && false !== strpos( $headers[0], '200 OK' ); 617 } -
blogcard-for-wp/trunk/build/block.json
r3357123 r3418932 5 5 "version": "2.0.0", 6 6 "title": "ブログカード", 7 "category": " text",7 "category": "su-blocks", 8 8 "icon": "admin-links", 9 9 "description": "URLを入力してブログカードを生成するブロックです。", -
blogcard-for-wp/trunk/build/index-rtl.css
r3357123 r3418932 1 .wp-blo gcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;right:0;overflow-y:auto;position:absolute;left:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-right:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-left:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{display:-webkit-box;overflow:hidden;-webkit-line-clamp:2;color:#666;font-size:12px;line-height:1.4;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto}1 .wp-block-blogcard.is-selected{padding:1rem}.wp-block-blogcard:not(.is-selected){& .wp-blogcard{margin:0!important}}.wp-blogcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;right:0;overflow-y:auto;position:absolute;left:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-right:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-left:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{color:#666;display:-webkit-box;font-size:12px;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto} -
blogcard-for-wp/trunk/build/index.asset.php
r3357123 r3418932 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => ' 1e9fb9c52b6f2f7e3826');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'e395c5c79d9305558fa9'); -
blogcard-for-wp/trunk/build/index.css
r3357123 r3418932 1 .wp-blo gcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;left:0;overflow-y:auto;position:absolute;right:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-left:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-right:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{display:-webkit-box;overflow:hidden;-webkit-line-clamp:2;color:#666;font-size:12px;line-height:1.4;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto}1 .wp-block-blogcard.is-selected{padding:1rem}.wp-block-blogcard:not(.is-selected){& .wp-blogcard{margin:0!important}}.wp-blogcard-item{pointer-events:none}.wpbc-search-results{background:#fff;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 10px #0000001a;left:0;overflow-y:auto;position:absolute;right:0;top:100%;z-index:1000}.wpbc-search-results-header{align-items:center;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;font-size:12px;font-weight:600;justify-content:space-between;padding:8px 12px}.wpbc-search-close{align-items:center;background:none;border:none;border-radius:50%;color:#666;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;padding:0;transition:all .2s;width:24px}.wpbc-search-close:hover{background:#e9ecef;color:#333}.wpbc-search-results-list{margin:0;max-height:400px;overflow-y:auto;padding-left:0}.wpbc-search-result-item{border-bottom:1px solid #f0f0f0;cursor:pointer;display:flex;flex-direction:row-reverse;padding:12px;transition:background-color .2s}.wpbc-search-result-item:hover{background-color:#f8f9fa}.wpbc-search-result-item:last-child{border-bottom:none}.wpbc-search-result-thumbnail{flex-shrink:0;height:50px;margin-right:12px}.wpbc-search-result-thumbnail img{border-radius:4px;height:50px;object-fit:cover;width:50px}.wpbc-search-result-content{flex:1;min-width:0}.wpbc-search-result-title{color:#333;font-size:14px;font-weight:600;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wpbc-search-result-meta{color:#666;display:flex;font-size:12px;gap:8px;margin-bottom:4px}.wpbc-search-result-type{background:#e9ecef;border-radius:3px;font-size:11px;padding:2px 6px}.wpbc-search-result-excerpt{color:#666;display:-webkit-box;font-size:12px;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.wpbc-search-loading,.wpbc-search-no-results{color:#666;font-size:14px;padding:20px;text-align:center}.wpbc-show-link-button{cursor:pointer;font-size:.75rem}.blogcard-url-input{background:#fff;border:1px solid #ddd;border-radius:4px;padding:10px}.blogcard-actions{text-align:center}.blogcard-actions .button{margin:0 5px}.editor-post-featured-image__toggle:has(* img){aspect-ratio:16/9;height:auto} -
blogcard-for-wp/trunk/build/index.js
r3357123 r3418932 1 (()=>{"use strict";var e,t={ 618:(e,t,n)=>{const l=window.wp.blocks,r=window.React,a=window.wp.blockEditor,c=window.wp.element,o=window.wp.i18n,s=window.wp.apiFetch;var i=n.n(s);const p=window.wp.components;function u(e){return!(!e||e.length<10)&&URL.canParse(e)}function m(e){if(!e)return!1;try{const t=new URL(e),n=new URL(window.location.href).hostname,l=t.hostname;return n===l||!(!n.endsWith("."+l)&&!l.endsWith("."+n))}catch{return!1}}function d({results:e,isLoading:t,onSelect:n,onClose:l,error:a}){return(0,r.createElement)("div",{className:"wpbc-search-results"},(0,r.createElement)("div",{className:"wpbc-search-results-header"},(0,r.createElement)("span",null,(0,o.__)("検索結果","wpbc")),(0,r.createElement)("button",{type:"button",className:"wpbc-search-close",onClick:l},"×")),t?(0,r.createElement)("div",{className:"wpbc-search-loading"},(0,r.createElement)(p.Spinner,null),(0,o.__)("検索中...","wpbc")):a?(0,r.createElement)("div",{className:"wpbc-search-error",style:{color:"#cc0000",padding:"10px",textAlign:"center"}},a):e&&e.length>0?(0,r.createElement)("ul",{className:"wpbc-search-results-list"},e.map(e=>(0,r.createElement)("li",{key:e.id,className:"wpbc-search-result-item",onClick:()=>n(e)},e.thumbnail&&(0,r.createElement)("div",{className:"wpbc-search-result-thumbnail"},(0,r.createElement)("img",{src:e.thumbnail,alt:e.title,width:"60",height:"60"})),(0,r.createElement)("div",{className:"wpbc-search-result-content"},(0,r.createElement)("div",{className:"wpbc-search-result-title"},e.title),(0,r.createElement)("div",{className:"wpbc-search-result-meta"},(0,r.createElement)("span",{className:"wpbc-search-result-type"},"post"===e.type?(0,o.__)("投稿","wpbc"):(0,o.__)("固定ページ","wpbc")),(0,r.createElement)("span",{className:"wpbc-search-result-date"},e.date)),e.excerpt&&(0,r.createElement)("div",{className:"wpbc-search-result-excerpt"},e.excerpt))))):(0,r.createElement)("div",{className:"wpbc-search-no-results"},(0,o.__)("検索結果が見つかりませんでした","wpbc")))}function b({value:e,onChange:t,onKeyDown:n,onSelectResult:l,placeholder:a=(0,o.__)("https://example.com or キーワード","wpbc"),label:s=(0,o.__)("URLまたはサイト内検索","wpbc"),showSearch:m=!1,disabled:b=!1}){const h=u(e),[w,g]=(0,c.useState)([]),[_,f]=(0,c.useState)(!1),[E,v]=(0,c.useState)(!1),[y,C]=(0,c.useState)(""),x=(0,c.useRef)(null),k=(0,c.useCallback)(async e=>{if(!e||e.length<2)return g([]),v(!1),void C("");x.current&&x.current.abort(),x.current=new AbortController,f(!0),C("");try{const t=await i()({path:`/wpbc/v1/search?q=${encodeURIComponent(e)}`,method:"GET",signal:x.current.signal});t&&t.success?(g(t.data||[]),v(!0),C("")):(g([]),v(!1),C((0,o.__)("検索に失敗しました。","wpbc")))}catch(e){if("AbortError"===e.name)return;g([]),v(!1),C((0,o.__)("検索中にエラーが発生しました。","wpbc"))}finally{f(!1)}},[]);return(0,c.useEffect)(()=>{if(m&&!b){if(!e||""===e.trim())return g([]),v(!1),void C("");if(!h){const t=setTimeout(()=>{k(e)},300);return()=>{clearTimeout(t),x.current&&x.current.abort()}}g([]),v(!1),C("")}},[e,m,b,h,k]),(0,c.useEffect)(()=>{h&&(g([]),v(!1),f(!1),C(""))},[h]),(0,c.useEffect)(()=>()=>{x.current&&x.current.abort()},[]),(0,r.createElement)("div",{style:{position:"relative"}},(0,r.createElement)(p.TextControl,{label:s,value:e,onChange:t,onKeyDown:t=>{"Enter"===t.key&&(t.preventDefault(),u(e)&&n&&n(t))},placeholder:a}),m&&!b&&e&&""!==e.trim()&&!u(e)&&(E||y)&&(0,r.createElement)(d,{results:w,isLoading:_,onSelect:e=>{t(e.url),l&&l(e),v(!1),g([])},onClose:()=>{v(!1),g([])},error:y}))}function h({attributes:e}){const{url:t,title:n,description:l,thumbnailUrl:a,thumbnailId:c,showThumbnail:o,favicon:s,target:i="_blank",noopener:p=!0,nofollow:u=!1,noreferrer:m=!1,sponsored:d=!1,ugc:b=!1,isSelected:h=!1}=e,w=[];p&&w.push("noopener"),m&&w.push("noreferrer"),u&&w.push("nofollow"),d&&w.push("sponsored"),b&&w.push("ugc");const g=w.length>0?w.join(" "):"",_=a,f=o&&_;return(0,r.createElement)("article",{className:"wp-blogcard",cite:t},(0,r.createElement)("a",{href:t,target:i||void 0,rel:g||void 0,className:"wp-blogcard-item"},f&&(0,r.createElement)("figure",{className:"wp-blogcard-figure"},(0,r.createElement)("img",{src:_,alt:"","aria-hidden":"true"})),(0,r.createElement)("div",{className:"wp-blogcard-content"},(0,r.createElement)("div",{className:"wp-blogcard-title"},n),(0,r.createElement)("div",{className:"wp-blogcard-description"},l),(0,r.createElement)("div",{className:"wp-blogcard-cite"},s&&(0,r.createElement)("img",{className:"wp-blogcard-favicon",src:s,alt:"","aria-hidden":"true"}),(0,r.createElement)("div",{className:"wp-blogcard-domain"},function(e){try{return new URL(e).hostname}catch{return""}}(t))))))}function w({url:e,cached:t,onOpenLink:n}){return(0,r.createElement)("div",{className:"blogcard-preview-footer",style:{marginTop:"10px",display:"flex",justifyContent:"space-between",alignItems:"center"}},(0,r.createElement)("div",{style:{fontSize:"12px",color:"#666",display:"flex",alignItems:"center",gap:"4px"}},t&&(0,r.createElement)("span",{style:{padding:"2px 6px",borderRadius:"3px",fontSize:"10px",fontWeight:"bold"}},"CACHED")),(0,r.createElement)(p.Button,{type:"button",className:"wpbc-show-link-button",onClick:()=>n(e),disabled:!e,variant:"secondary",size:"small"},(0,o.__)("リンク先を表示","wpbc")))}function g({error:e,errorCode:t}){return e?(0,r.createElement)("div",{style:{color:"#cc0000",fontSize:"14px",margin:"8px 0",padding:"8px 0"}},e,t&&(0,r.createElement)("span",{style:{marginLeft:"8px",color:"#666",fontSize:"12px"}},"(",t,")")):null}function _({message:e=(0,o.__)("メタデータを取得中...","wpbc")}){return(0,r.createElement)("div",{style:{textAlign:"center",padding:"1.25rem"}},(0,r.createElement)(p.Spinner,null),(0,r.createElement)("p",{style:{fontSize:"0.875rem"}},e))}function f({attributes:e,setAttributes:t,inputUrl:n,setInputUrl:l,handleKeyDown:c}){const{target:s,noopener:i,nofollow:u,noreferrer:m,sponsored:d,ugc:b,showThumbnail:h,title:w,description:g,thumbnailUrl:_,thumbnailId:f}=e,v=()=>f&&_?(0,r.createElement)("div",{style:{width:"100%",aspectRatio:"16/9",overflow:"hidden",borderRadius:"4px",backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center"}},(0,r.createElement)("img",{src:_,alt:"",style:{width:"100%",height:"100%",objectFit:"cover"}})):(0,r.createElement)("div",{style:{backgroundColor:"#f0f0f0",borderRadius:"4px",display:"flex",alignItems:"center",justifyContent:"center",color:"#666",fontSize:"14px"}},(0,o.__)("サムネイルを設定","wpbc"));return(0,r.createElement)(a.InspectorControls,null,(0,r.createElement)(p.PanelBody,{title:(0,o.__)("ブロック設定","wpbc"),initialOpen:!0},(0,r.createElement)(p.TextControl,{label:(0,o.__)("URL","wpbc"),value:n,onChange:e=>l(e),onKeyDown:c,placeholder:(0,o.__)("https://example.com or キーワード","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("TARGET属性","wpbc")),(0,r.createElement)(p.SelectControl,{label:(0,o.__)("TARGET属性","wpbc"),value:s,onChange:e=>{t("_blank"===e?{target:e,noopener:!0}:{target:e})},options:[{label:(0,o.__)("なし","wpbc"),value:""},{label:(0,o.__)("_blank(別タブ)","wpbc"),value:"_blank"},{label:(0,o.__)("_self (同じタブ)","wpbc"),value:"_self"}]}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("Rel属性","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("noopener を追加","wpbc"),checked:i,disabled:"_blank"===s,onChange:e=>t({noopener:e})}),"_blank"===s&&(0,r.createElement)("p",{style:{fontSize:"12px",color:"#666",margin:"8px 0"}},(0,o.__)("_blankの場合はセキュリティ上noopenerが必須です","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=nofollow を追加","wpbc"),checked:u,onChange:e=>t({nofollow:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=noreferrer を追加","wpbc"),checked:m,onChange:e=>t({noreferrer:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=sponsored を追加","wpbc"),checked:d,onChange:e=>t({sponsored:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=ugc を追加","wpbc"),checked:b,onChange:e=>t({ugc:e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("サムネイル","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("サムネイルを表示しない","wpbc"),checked:!h,onChange:e=>t({showThumbnail:!e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("タイトルを手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("タイトル","wpbc"),value:w,onChange:e=>t({title:e}),placeholder:(0,o.__)("タイトルを入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("説明文を手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("説明文","wpbc"),value:g,onChange:e=>t({description:e}),placeholder:(0,o.__)("説明文を入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)(p.BaseControl,{label:(0,o.__)("サムネイルを手動で設定","wpbc")},(0,r.createElement)(a.MediaUploadCheck,null,(0,r.createElement)(a.MediaUpload,{onSelect:e=>{t({thumbnailId:e.id,thumbnailUrl:e.url,showThumbnail:!0})},allowedTypes:["image"],value:f,render:({open:e})=>(0,r.createElement)(p.Button,{onClick:e,className:"editor-post-featured-image__toggle"},(0,r.createElement)(v,null))})),(0,r.createElement)(p.Button,{style:{marginTop:"0.5rem"},className:"is-tertiary",onClick:()=>{t({thumbnailId:0,thumbnailUrl:"",showThumbnail:!1})}},(0,o.__)("クリア","wpbc"))),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("キャッシュ管理","wpbc")),(0,r.createElement)("div",{style:{display:"flex",gap:"10px",flexWrap:"wrap"}},(0,r.createElement)(p.Button,{secondary:!0,onClick:E("plugin"),style:{fontSize:"12px"}},(0,o.__)("ブログカードキャッシュクリア","wpbc")),(0,r.createElement)(p.Button,{isDestructive:!0,onClick:()=>{confirm((0,o.__)("全てのWordPressキャッシュをクリアしますか?","wpbc"))&&E("all")()},style:{fontSize:"12px"}},(0,o.__)("全キャッシュクリア","wpbc")))))}function E(e="plugin"){return async()=>{try{const t=await i()({path:"/wpbc/v1/clear-cache",method:"POST",data:{type:e}});t.success?alert(t.message):alert((0,o.__)("キャッシュのクリアに失敗しました。","wpbc"))}catch(e){console.error("Cache clear error:",e),alert((0,o.__)("キャッシュのクリア中にエラーが発生しました。","wpbc"))}}}(0,l.registerBlockType)("su/blogcard",{edit:function({attributes:e,setAttributes:t}){const{url:n,target:l,noopener:s,nofollow:p,noreferrer:d,sponsored:E,ugc:v,showThumbnail:y,title:C,description:x,thumbnailUrl:k,thumbnailId:T,favicon:N}=e,[S,R]=(0,c.useState)(n||""),[U,O]=(0,c.useState)(!1),[I,D]=(0,c.useState)(""),[L,z]=(0,c.useState)(""),[A,B]=(0,c.useState)(!1),j=(0,a.useBlockProps)({className:"wp-block-blogcard"}),K=j.className?.includes("is-selected")||!1,P=e=>{"Enter"===e.key&&(e.preventDefault(),u(S)&&(S!==n&&t({url:"",title:"",description:"",thumbnailUrl:"",showThumbnail:!1,favicon:""}),(async()=>{if(!S||S.trim().length<10||!u(S))return D(""),void z("");O(!0),D(""),z("");try{const e=m(S)?`/wpbc/v1/internal-metadata?url=${encodeURIComponent(S)}`:"/wpbc/v1/metadata",n=await i()({path:e,method:m(S)?"GET":"POST",data:m(S)?void 0:{url:S}});if(n.success){const e=n.data;t({url:S,title:e.title||S,description:e.description||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""}),B(!0===e.cached),D(""),z("")}else{const e=n.message||n.data?.message||(0,o.__)("メタデータの取得に失敗しました。","wpbc"),t=n.code||n.data?.code||"UNKNOWN_ERROR";D(e),z(t)}}catch(e){const t=e.status||e.code||"NETWORK_ERROR";let n=e.message||(0,o.__)("メタデータの取得中にエラーが発生しました。","wpbc");e.data&&e.data.message&&(n=e.data.message),D(n),z(t)}finally{O(!1)}})()))},W=e=>{R(e.url),D(""),z(""),B(!1),t({url:e.url,title:e.title||e.url,description:e.excerpt||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""})};return(0,r.createElement)("div",{...j},(0,r.createElement)(f,{attributes:e,setAttributes:t,inputUrl:S,setInputUrl:R,handleKeyDown:P}),n||I?(0,r.createElement)("div",null,U&&(0,r.createElement)(_,null),K&&(0,r.createElement)(b,{key:"selected-search-input",value:S,onChange:e=>R(e),onKeyDown:P,onSelectResult:W,showSearch:!0,disabled:!1}),(0,r.createElement)(g,{error:I,errorCode:L}),!U&&!I&&n&&(0,r.createElement)("div",{className:"blogcard-preview"},(0,r.createElement)(h,{attributes:{url:n,title:C,description:x,thumbnailUrl:k,showThumbnail:y,favicon:N,isSelected:K}}),K&&(0,r.createElement)(w,{url:n,cached:A,onOpenLink:e=>{window.open(e,"_blank")}}))):(0,r.createElement)(b,{key:"placeholder-search-input",value:S,onChange:e=>R(e),onKeyDown:P,onSelectResult:W,showSearch:!0,disabled:!1}))},save:function({attributes:e}){const t=a.useBlockProps.save();return(0,r.createElement)("div",{...t},(0,r.createElement)(h,{attributes:e}))}})}},n={};function l(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,l),a.exports}l.m=t,e=[],l.O=(t,n,r,a)=>{if(!n){var c=1/0;for(p=0;p<e.length;p++){for(var[n,r,a]=e[p],o=!0,s=0;s<n.length;s++)(!1&a||c>=a)&&Object.keys(l.O).every(e=>l.O[e](n[s]))?n.splice(s--,1):(o=!1,a<c&&(c=a));if(o){e.splice(p--,1);var i=r();void 0!==i&&(t=i)}}return t}a=a||0;for(var p=e.length;p>0&&e[p-1][2]>a;p--)e[p]=e[p-1];e[p]=[n,r,a]},l.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return l.d(t,{a:t}),t},l.d=(e,t)=>{for(var n in t)l.o(t,n)&&!l.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var r,a,[c,o,s]=n,i=0;if(c.some(t=>0!==e[t])){for(r in o)l.o(o,r)&&(l.m[r]=o[r]);if(s)var p=s(l)}for(t&&t(n);i<c.length;i++)a=c[i],l.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return l.O(p)},n=globalThis.webpackChunkblogcard_for_wp=globalThis.webpackChunkblogcard_for_wp||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var r=l.O(void 0,[350],()=>l(618));r=l.O(r)})();1 (()=>{"use strict";var e,t={486:(e,t,n)=>{const l=window.wp.blocks,r=window.React,a=window.wp.blockEditor,c=window.wp.element,o=window.wp.i18n,s=window.wp.apiFetch;var i=n.n(s);const p=window.wp.components;function u(e){return!(!e||e.length<10)&&URL.canParse(e)}function m(e){if(!e)return!1;try{const t=new URL(e),n=new URL(window.location.href).hostname,l=t.hostname;return n===l||!(!n.endsWith("."+l)&&!l.endsWith("."+n))}catch{return!1}}function d({results:e,isLoading:t,onSelect:n,onClose:l,error:a}){return(0,r.createElement)("div",{className:"wpbc-search-results"},(0,r.createElement)("div",{className:"wpbc-search-results-header"},(0,r.createElement)("span",null,(0,o.__)("検索結果","wpbc")),(0,r.createElement)("button",{type:"button",className:"wpbc-search-close",onClick:l},"×")),t?(0,r.createElement)("div",{className:"wpbc-search-loading"},(0,r.createElement)(p.Spinner,null),(0,o.__)("検索中...","wpbc")):a?(0,r.createElement)("div",{className:"wpbc-search-error",style:{color:"#cc0000",padding:"10px",textAlign:"center"}},a):e&&e.length>0?(0,r.createElement)("ul",{className:"wpbc-search-results-list"},e.map(e=>(0,r.createElement)("li",{key:e.id,className:"wpbc-search-result-item",onClick:()=>n(e)},e.thumbnail&&(0,r.createElement)("div",{className:"wpbc-search-result-thumbnail"},(0,r.createElement)("img",{src:e.thumbnail,alt:e.title,width:"60",height:"60"})),(0,r.createElement)("div",{className:"wpbc-search-result-content"},(0,r.createElement)("div",{className:"wpbc-search-result-title"},e.title),(0,r.createElement)("div",{className:"wpbc-search-result-meta"},(0,r.createElement)("span",{className:"wpbc-search-result-type"},"post"===e.type?(0,o.__)("投稿","wpbc"):(0,o.__)("固定ページ","wpbc")),(0,r.createElement)("span",{className:"wpbc-search-result-date"},e.date)),e.excerpt&&(0,r.createElement)("div",{className:"wpbc-search-result-excerpt"},e.excerpt))))):(0,r.createElement)("div",{className:"wpbc-search-no-results"},(0,o.__)("検索結果が見つかりませんでした","wpbc")))}function b({value:e,onChange:t,onKeyDown:n,onSelectResult:l,placeholder:a=(0,o.__)("https://example.com or キーワード","wpbc"),label:s=(0,o.__)("URLまたはサイト内検索","wpbc"),showSearch:m=!1,disabled:b=!1}){const h=u(e),[w,g]=(0,c.useState)([]),[_,E]=(0,c.useState)(!1),[f,v]=(0,c.useState)(!1),[y,x]=(0,c.useState)(""),C=(0,c.useRef)(null),k=(0,c.useCallback)(async e=>{if(!e||e.length<2)return g([]),v(!1),void x("");C.current&&C.current.abort(),C.current=new AbortController,E(!0),x("");try{const t=await i()({path:`/wpbc/v1/search?q=${encodeURIComponent(e)}`,method:"GET",signal:C.current.signal});t&&t.success?(g(t.data||[]),v(!0),x("")):(g([]),v(!1),x((0,o.__)("検索に失敗しました。","wpbc")))}catch(e){if("AbortError"===e.name)return;g([]),v(!1),x((0,o.__)("検索中にエラーが発生しました。","wpbc"))}finally{E(!1)}},[]);return(0,c.useEffect)(()=>{if(m&&!b){if(!e||""===e.trim())return g([]),v(!1),void x("");if(!h){const t=setTimeout(()=>{k(e)},300);return()=>{clearTimeout(t),C.current&&C.current.abort()}}g([]),v(!1),x("")}},[e,m,b,h,k]),(0,c.useEffect)(()=>{h&&(g([]),v(!1),E(!1),x(""))},[h]),(0,c.useEffect)(()=>()=>{C.current&&C.current.abort()},[]),(0,r.createElement)("div",{style:{position:"relative"}},(0,r.createElement)(p.TextControl,{label:s,value:e,onChange:t,onKeyDown:t=>{"Enter"===t.key&&(t.preventDefault(),u(e)&&n&&n(t))},placeholder:a,__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0}),m&&!b&&e&&""!==e.trim()&&!u(e)&&(f||y)&&(0,r.createElement)(d,{results:w,isLoading:_,onSelect:e=>{t(e.url),l&&l(e),v(!1),g([])},onClose:()=>{v(!1),g([])},error:y}))}function h({attributes:e}){const{url:t,title:n,description:l,thumbnailUrl:a,thumbnailId:c,showThumbnail:o,favicon:s,target:i="_blank",noopener:p=!0,nofollow:u=!1,noreferrer:m=!1,sponsored:d=!1,ugc:b=!1,isSelected:h=!1}=e,w=[];p&&w.push("noopener"),m&&w.push("noreferrer"),u&&w.push("nofollow"),d&&w.push("sponsored"),b&&w.push("ugc");const g=w.length>0?w.join(" "):"",_=a,E=o&&_;return(0,r.createElement)("article",{className:"wp-blogcard",cite:t},(0,r.createElement)("a",{href:t,target:i||void 0,rel:g||void 0,className:"wp-blogcard-item"},E&&(0,r.createElement)("figure",{className:"wp-blogcard-figure"},(0,r.createElement)("img",{src:_,alt:"","aria-hidden":"true"})),(0,r.createElement)("div",{className:"wp-blogcard-content"},(0,r.createElement)("div",{className:"wp-blogcard-title"},n),(0,r.createElement)("div",{className:"wp-blogcard-description"},l),(0,r.createElement)("div",{className:"wp-blogcard-cite"},s&&(0,r.createElement)("img",{className:"wp-blogcard-favicon",src:s,alt:"","aria-hidden":"true"}),(0,r.createElement)("div",{className:"wp-blogcard-domain"},function(e){try{return new URL(e).hostname}catch{return""}}(t))))))}function w({url:e,cached:t,onOpenLink:n}){return(0,r.createElement)("div",{className:"blogcard-preview-footer",style:{marginTop:"10px",display:"flex",justifyContent:"space-between",alignItems:"center"}},(0,r.createElement)("div",{style:{fontSize:"12px",color:"#666",display:"flex",alignItems:"center",gap:"4px"}},t&&(0,r.createElement)("span",{style:{padding:"2px 6px",borderRadius:"3px",fontSize:"10px",fontWeight:"bold"}},"CACHED")),(0,r.createElement)(p.Button,{type:"button",className:"wpbc-show-link-button",onClick:()=>n(e),disabled:!e,variant:"secondary",size:"small"},(0,o.__)("リンク先を表示","wpbc")))}function g({error:e,errorCode:t}){return e?(0,r.createElement)("div",{style:{color:"#cc0000",fontSize:"14px",margin:"8px 0",padding:"8px 0"}},e,t&&(0,r.createElement)("span",{style:{marginLeft:"8px",color:"#666",fontSize:"12px"}},"(",t,")")):null}function _({message:e=(0,o.__)("メタデータを取得中...","wpbc")}){return(0,r.createElement)("div",{style:{textAlign:"center",padding:"1.25rem"}},(0,r.createElement)(p.Spinner,null),(0,r.createElement)("p",{style:{fontSize:"0.875rem"}},e))}function E({attributes:e,setAttributes:t,inputUrl:n,setInputUrl:l,handleKeyDown:c}){const{target:s,noopener:i,nofollow:u,noreferrer:m,sponsored:d,ugc:b,showThumbnail:h,title:w,description:g,thumbnailUrl:_,thumbnailId:E}=e,v=()=>E&&_?(0,r.createElement)("div",{style:{width:"100%",aspectRatio:"16/9",overflow:"hidden",borderRadius:"4px",backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center"}},(0,r.createElement)("img",{src:_,alt:"",style:{width:"100%",height:"100%",objectFit:"cover"}})):(0,r.createElement)("div",{style:{backgroundColor:"#f0f0f0",borderRadius:"4px",display:"flex",alignItems:"center",justifyContent:"center",color:"#666",fontSize:"14px"}},(0,o.__)("サムネイルを設定","wpbc"));return(0,r.createElement)(a.InspectorControls,null,(0,r.createElement)(p.PanelBody,{title:(0,o.__)("ブロック設定","wpbc"),initialOpen:!0},(0,r.createElement)(p.TextControl,{label:(0,o.__)("URL","wpbc"),value:n,onChange:e=>l(e),onKeyDown:c,placeholder:(0,o.__)("https://example.com or キーワード","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("TARGET属性","wpbc")),(0,r.createElement)(p.SelectControl,{label:(0,o.__)("TARGET属性","wpbc"),value:s,onChange:e=>{t("_blank"===e?{target:e,noopener:!0}:{target:e})},options:[{label:(0,o.__)("なし","wpbc"),value:""},{label:(0,o.__)("_blank(別タブ)","wpbc"),value:"_blank"},{label:(0,o.__)("_self (同じタブ)","wpbc"),value:"_self"}]}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("Rel属性","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("noopener を追加","wpbc"),checked:i,disabled:"_blank"===s,onChange:e=>t({noopener:e})}),"_blank"===s&&(0,r.createElement)("p",{style:{fontSize:"12px",color:"#666",margin:"8px 0"}},(0,o.__)("_blankの場合はセキュリティ上noopenerが必須です","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=nofollow を追加","wpbc"),checked:u,onChange:e=>t({nofollow:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=noreferrer を追加","wpbc"),checked:m,onChange:e=>t({noreferrer:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=sponsored を追加","wpbc"),checked:d,onChange:e=>t({sponsored:e})}),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("rel=ugc を追加","wpbc"),checked:b,onChange:e=>t({ugc:e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("サムネイル","wpbc")),(0,r.createElement)(p.ToggleControl,{label:(0,o.__)("サムネイルを表示しない","wpbc"),checked:!h,onChange:e=>t({showThumbnail:!e})}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("タイトルを手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("タイトル","wpbc"),value:w,onChange:e=>t({title:e}),placeholder:(0,o.__)("タイトルを入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("説明文を手動で入力","wpbc")),(0,r.createElement)(p.TextControl,{label:(0,o.__)("説明文","wpbc"),value:g,onChange:e=>t({description:e}),placeholder:(0,o.__)("説明文を入力","wpbc")}),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)(p.BaseControl,{label:(0,o.__)("サムネイルを手動で設定","wpbc")},(0,r.createElement)(a.MediaUploadCheck,null,(0,r.createElement)(a.MediaUpload,{onSelect:e=>{t({thumbnailId:e.id,thumbnailUrl:e.url,showThumbnail:!0})},allowedTypes:["image"],value:E,render:({open:e})=>(0,r.createElement)(p.Button,{onClick:e,className:"editor-post-featured-image__toggle"},(0,r.createElement)(v,null))})),(0,r.createElement)(p.Button,{style:{marginTop:"0.5rem"},className:"is-tertiary",onClick:()=>{t({thumbnailId:0,thumbnailUrl:"",showThumbnail:!1})}},(0,o.__)("クリア","wpbc"))),(0,r.createElement)("hr",{style:{margin:"20px 0"}}),(0,r.createElement)("h4",null,(0,o.__)("キャッシュ管理","wpbc")),(0,r.createElement)("div",{style:{display:"flex",gap:"10px",flexWrap:"wrap"}},(0,r.createElement)(p.Button,{secondary:!0,onClick:f("plugin"),style:{fontSize:"12px"}},(0,o.__)("ブログカードキャッシュクリア","wpbc")),(0,r.createElement)(p.Button,{isDestructive:!0,onClick:()=>{confirm((0,o.__)("全てのWordPressキャッシュをクリアしますか?","wpbc"))&&f("all")()},style:{fontSize:"12px"}},(0,o.__)("全キャッシュクリア","wpbc")))))}function f(e="plugin"){return async()=>{try{const t=await i()({path:"/wpbc/v1/clear-cache",method:"POST",data:{type:e}});t.success?alert(t.message):alert((0,o.__)("キャッシュのクリアに失敗しました。","wpbc"))}catch(e){console.error("Cache clear error:",e),alert((0,o.__)("キャッシュのクリア中にエラーが発生しました。","wpbc"))}}}(0,l.registerBlockType)("su/blogcard",{edit:function({attributes:e,setAttributes:t}){const{url:n,showThumbnail:l,title:s,description:p,thumbnailUrl:d,favicon:f}=e,[v,y]=(0,c.useState)(n||""),[x,C]=(0,c.useState)(!1),[k,T]=(0,c.useState)(""),[N,S]=(0,c.useState)(""),[R,U]=(0,c.useState)(!1),O=(0,a.useBlockProps)({className:"wp-block-blogcard"}),I=O.className?.includes("is-selected")||!1,D=e=>{"Enter"===e.key&&(e.preventDefault(),u(v)&&(v!==n&&t({url:"",title:"",description:"",thumbnailUrl:"",showThumbnail:!1,favicon:""}),(async()=>{if(!v||v.trim().length<10||!u(v))return T(""),void S("");C(!0),T(""),S("");try{const e=m(v)?`/wpbc/v1/internal-metadata?url=${encodeURIComponent(v)}`:"/wpbc/v1/metadata",n=await i()({path:e,method:m(v)?"GET":"POST",data:m(v)?void 0:{url:v}});if(n.success){const e=n.data;t({url:v,title:e.title||v,description:e.description||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""}),U(!0===e.cached),T(""),S("")}else{const e=n.message||n.data?.message||(0,o.__)("メタデータの取得に失敗しました。","wpbc"),t=n.code||n.data?.code||"UNKNOWN_ERROR";T(e),S(t)}}catch(e){const t=e.status||e.code||"NETWORK_ERROR";let n=e.message||(0,o.__)("メタデータの取得中にエラーが発生しました。","wpbc");e.data&&e.data.message&&(n=e.data.message),T(n),S(t)}finally{C(!1)}})()))},z=e=>{y(e.url),T(""),S(""),U(!1),t({url:e.url,title:e.title||e.url,description:e.excerpt||"",thumbnailUrl:e.thumbnail||"",showThumbnail:!!e.thumbnail,favicon:e.favicon||""})};return(0,r.createElement)("div",{...O},(0,r.createElement)(E,{attributes:e,setAttributes:t,inputUrl:v,setInputUrl:y,handleKeyDown:D}),n||k?(0,r.createElement)("div",null,x&&(0,r.createElement)(_,null),I&&(0,r.createElement)(b,{key:"selected-search-input",value:v,onChange:e=>y(e),onKeyDown:D,onSelectResult:z,showSearch:!0,disabled:!1}),(0,r.createElement)(g,{error:k,errorCode:N}),!x&&!k&&n&&(0,r.createElement)("div",{className:"blogcard-preview"},(0,r.createElement)(h,{attributes:{url:n,title:s,description:p,thumbnailUrl:d,showThumbnail:l,favicon:f,isSelected:I}}),I&&(0,r.createElement)(w,{url:n,cached:R,onOpenLink:e=>{window.open(e,"_blank")}}))):(0,r.createElement)(b,{key:"placeholder-search-input",value:v,onChange:e=>y(e),onKeyDown:D,onSelectResult:z,showSearch:!0,disabled:!1}))},save:function({attributes:e}){const t=a.useBlockProps.save();return(0,r.createElement)("div",{...t},(0,r.createElement)(h,{attributes:e}))}})}},n={};function l(e){var r=n[e];if(void 0!==r)return r.exports;var a=n[e]={exports:{}};return t[e](a,a.exports,l),a.exports}l.m=t,e=[],l.O=(t,n,r,a)=>{if(!n){var c=1/0;for(p=0;p<e.length;p++){for(var[n,r,a]=e[p],o=!0,s=0;s<n.length;s++)(!1&a||c>=a)&&Object.keys(l.O).every(e=>l.O[e](n[s]))?n.splice(s--,1):(o=!1,a<c&&(c=a));if(o){e.splice(p--,1);var i=r();void 0!==i&&(t=i)}}return t}a=a||0;for(var p=e.length;p>0&&e[p-1][2]>a;p--)e[p]=e[p-1];e[p]=[n,r,a]},l.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return l.d(t,{a:t}),t},l.d=(e,t)=>{for(var n in t)l.o(t,n)&&!l.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},l.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={57:0,350:0};l.O.j=t=>0===e[t];var t=(t,n)=>{var r,a,[c,o,s]=n,i=0;if(c.some(t=>0!==e[t])){for(r in o)l.o(o,r)&&(l.m[r]=o[r]);if(s)var p=s(l)}for(t&&t(n);i<c.length;i++)a=c[i],l.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return l.O(p)},n=globalThis.webpackChunkblogcard_for_wp=globalThis.webpackChunkblogcard_for_wp||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))})();var r=l.O(void 0,[350],()=>l(486));r=l.O(r)})(); -
blogcard-for-wp/trunk/build/style-index-rtl.css
r3357123 r3418932 1 :where(.wp-blo gcard){container-type:inline-size;margin-block:1.5rem}.wp-blogcard-item{--color-dark-gray:#18181b;--color-gray:#52525c;--color-light-gray:#e4e4e7;--color-base:#f4f4f5;--color-background:#fff;--border-radius:0.5rem;--padding:0.875rem;--gap:1rem;--image-width:5rem;--image-height:5rem;--bg-color:pink;background-color:var(--bg-color);background-color:var(--color-background);border:1px solid var(--color-light-gray);border-radius:var(--border-radius);color:var(--color-dark-gray);display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none;transition:background-color .2s ease;&:hover{background-color:var(--color-base);color:var(--color-dark-gray)}}.dark .wp-blogcard-item{--color-background:#18181b;--color-dark-gray:#f4f4f5;--color-gray:#9f9fa9;--color-light-gray:#52525c;--color-base:#27272a}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:.5rem;height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;margin:0;overflow:hidden;-webkit-line-clamp:2;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-gray);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-left:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-gray);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard-item{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem;--bg-color:orange;--border-radius:0.75rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}}1 :where(.wp-block-su-blogcard){margin-block:1.5rem}.wp-block-su-blogcard{container-type:inline-size;--color-text:#18181b;--color-text-light:#71717b;--color-ui:#e4e4e7;--color-surface:#f4f4f5;--color-background:#fff;--border-radius:0.125rem;--block-padding:1rem 1.25rem;--gap:1rem;--image-width:5rem;--image-height:5rem}.dark .wp-block-su-blogcard{--color-text:#f4f4f5;--color-text-light:#9f9fa9;--color-ui:#27272a;--color-surface:#18181b;--color-background:#09090b;--color-white:#09090b}.wp-blogcard{background-color:var(--color-background);border:1px solid var(--color-ui);border-radius:var(--border-radius);color:var(--color-text);transition:background-color .2s ease;&:hover{background-color:var(--color-surface);color:var(--color-text)}}.wp-blogcard-item{display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none!important}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:var(--border-radius);height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{color:var(--color-text);font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;-webkit-line-clamp:2;margin:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-text-light);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-left:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-text-light);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}} -
blogcard-for-wp/trunk/build/style-index.css
r3357123 r3418932 1 :where(.wp-blo gcard){container-type:inline-size;margin-block:1.5rem}.wp-blogcard-item{--color-dark-gray:#18181b;--color-gray:#52525c;--color-light-gray:#e4e4e7;--color-base:#f4f4f5;--color-background:#fff;--border-radius:0.5rem;--padding:0.875rem;--gap:1rem;--image-width:5rem;--image-height:5rem;--bg-color:pink;background-color:var(--bg-color);background-color:var(--color-background);border:1px solid var(--color-light-gray);border-radius:var(--border-radius);color:var(--color-dark-gray);display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none;transition:background-color .2s ease;&:hover{background-color:var(--color-base);color:var(--color-dark-gray)}}.dark .wp-blogcard-item{--color-background:#18181b;--color-dark-gray:#f4f4f5;--color-gray:#9f9fa9;--color-light-gray:#52525c;--color-base:#27272a}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:.5rem;height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;margin:0;overflow:hidden;-webkit-line-clamp:2;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-gray);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-right:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-gray);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard-item{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem;--bg-color:orange;--border-radius:0.75rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}}1 :where(.wp-block-su-blogcard){margin-block:1.5rem}.wp-block-su-blogcard{container-type:inline-size;--color-text:#18181b;--color-text-light:#71717b;--color-ui:#e4e4e7;--color-surface:#f4f4f5;--color-background:#fff;--border-radius:0.125rem;--block-padding:1rem 1.25rem;--gap:1rem;--image-width:5rem;--image-height:5rem}.dark .wp-block-su-blogcard{--color-text:#f4f4f5;--color-text-light:#9f9fa9;--color-ui:#27272a;--color-surface:#18181b;--color-background:#09090b;--color-white:#09090b}.wp-blogcard{background-color:var(--color-background);border:1px solid var(--color-ui);border-radius:var(--border-radius);color:var(--color-text);transition:background-color .2s ease;&:hover{background-color:var(--color-surface);color:var(--color-text)}}.wp-blogcard-item{display:flex;flex-direction:row-reverse;gap:var(--gap);padding:var(--padding);text-decoration:none!important}.wp-blogcard-figure{flex-shrink:0;height:var(--image-height);margin:0;overflow:hidden;width:var(--image-width)}.wp-blogcard-figure img{border-radius:var(--border-radius);height:100%;object-fit:cover;width:100%}.wp-blogcard-content{display:flex;flex:1;flex-direction:column;gap:.5rem}.wp-blogcard-title{color:var(--color-text);font-size:.875rem;font-weight:700}.wp-blogcard-description,.wp-blogcard-title{display:-webkit-box;-webkit-line-clamp:2;margin:0;overflow:hidden;word-break:break-word;-webkit-box-orient:vertical}.wp-blogcard-description{color:var(--color-text-light);font-size:.75rem;hyphens:auto}.wp-blogcard-cite{align-items:center;color:#999;display:flex;font-size:.9rem;margin:0}.wp-blogcard-favicon{border-radius:.125rem;height:16px;margin-right:.5rem;width:16px}.wp-blogcard-domain{color:var(--color-text-light);font-size:.675rem}@container (min-width: 32rem){.wp-blogcard{--padding:1.25rem;--gap:1.25rem;--image-width:6rem;--image-height:6rem}.wp-blogcard-title{font-size:1rem}.wp-blogcard-domain{font-size:.75rem}} -
blogcard-for-wp/trunk/readme.txt
r3368864 r3418932 5 5 Tested up to: 6.8.2 6 6 Requires PHP: 7.4 7 Stable tag: 2.0. 27 Stable tag: 2.0.6 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 98 98 == Changelog == 99 99 100 = 2.0.4 = 101 * Update CSS. 102 100 103 = 2.0.0 = 101 104 * Significantly improved block editor UI
Note: See TracChangeset
for help on using the changeset viewer.