Plugin Directory

Changeset 3418932


Ignore:
Timestamp:
12/13/2025 01:59:29 PM (4 months ago)
Author:
ejointjp
Message:

Update to version 2.0.6 from GitHub

Location:
blogcard-for-wp
Files:
16 added
23 edited
1 copied

Legend:

Unmodified
Added
Removed
  • blogcard-for-wp/assets/screenshot-1.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • blogcard-for-wp/assets/screenshot-2.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • blogcard-for-wp/assets/screenshot-3.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • blogcard-for-wp/assets/screenshot-4.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • blogcard-for-wp/assets/screenshot-5.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • blogcard-for-wp/tags/2.0.6/blogcard-for-wp.php

    r3368864 r3418932  
    22
    33/**
    4  * Plugin Name: Blogcard for WP
     4 * Plugin Name: SU Blocks - Blogcard
    55 * Plugin URI: https://wordpress.org/plugins/blogcard-for-wp/
    66 * Description: URLを入力してブログカードを生成するブロックプラグイン
    7  * Version: 2.0.2
     7 * Version: 2.0.6
    88 * Author: Takashi Fujisaki
    99 * License: GPL v2 or later
     
    1212
    1313// 直接アクセスを防ぐ
    14 if (!defined('ABSPATH')) {
    15   exit;
     14if ( ! defined( 'ABSPATH' ) ) {
     15    exit;
    1616}
    1717
    1818// プラグインの定数
    19 define('WPBC_PLUGIN_PATH', plugin_dir_path(__FILE__));
     19define( 'WPBC_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
     20
     21/**
     22 * カスタムブロックカテゴリーを追加
     23 */
     24function 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}
     40add_filter( 'block_categories_all', 'wpbc_block_categories', 10, 2 );
    2041
    2142/**
     
    2344 */
    2445function 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}
     52add_action( 'init', 'wpbc_init' );
    3253
    3354/**
     
    3556 */
    3657function 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}
     134add_action( 'rest_api_init', 'wpbc_rest_api_init' );
    98135
    99136/**
    100137 * メタデータ取得のREST APIコールバック
    101138 */
    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   ];
     139function 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    );
    125162}
    126163
     
    128165 * 内部メタデータ取得のREST APIコールバック
    129166 */
    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   ];
     167function 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    );
    147184}
    148185
     
    150187 * URLが内部サイトのURLかどうかを判定する
    151188 */
    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_suffix
    180   ) {
    181     return true;
    182   }
    183 
    184   return false;
     189function 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;
    185222}
    186223
     
    188225 * メタデータを取得する関数
    189226 */
    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;
     227function 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;
    367410}
    368411
     
    370413 * サイト内検索のREST APIコールバック
    371414 */
    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   ];
     415function 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    );
    422465}
    423466
     
    425468 * 内部サイトのメタデータを取得する
    426469 */
    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   ];
     470function 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    );
    455498}
    456499
     
    458501 * キャッシュクリアのREST APIコールバック
    459502 */
    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   ];
     503function 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    );
    494537}
    495538
     
    497540 * ファビコンを取得する関数
    498541 */
    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;
     542function 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;
    543589}
    544590
     
    546592 * 相対URLを絶対URLに変換する
    547593 */
    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, '/');
     594function 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, '/' );
    563609}
    564610
     
    566612 * ファビコンが存在するかチェックする
    567613 */
    568 function wpbc_check_favicon_exists($url) {
    569   $headers = @get_headers($url, 1);
    570   return $headers && strpos($headers[0], '200 OK') !== false;
    571 }
     614function 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  
    55  "version": "2.0.0",
    66  "title": "ブログカード",
    7   "category": "text",
     7  "category": "su-blocks",
    88  "icon": "admin-links",
    99  "description": "URLを入力してブログカードを生成するブロックです。",
  • blogcard-for-wp/tags/2.0.6/build/index-rtl.css

    r3357123 r3418932  
    1 .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{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-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{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-blogcard){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-blogcard){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  
    55Tested up to: 6.8.2
    66Requires PHP: 7.4
    7 Stable tag: 2.0.2
     7Stable tag: 2.0.6
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    9898== Changelog ==
    9999
     100= 2.0.4 =
     101* Update CSS.
     102
    100103= 2.0.0 =
    101104* Significantly improved block editor UI
  • blogcard-for-wp/trunk/blogcard-for-wp.php

    r3368864 r3418932  
    22
    33/**
    4  * Plugin Name: Blogcard for WP
     4 * Plugin Name: SU Blocks - Blogcard
    55 * Plugin URI: https://wordpress.org/plugins/blogcard-for-wp/
    66 * Description: URLを入力してブログカードを生成するブロックプラグイン
    7  * Version: 2.0.2
     7 * Version: 2.0.6
    88 * Author: Takashi Fujisaki
    99 * License: GPL v2 or later
     
    1212
    1313// 直接アクセスを防ぐ
    14 if (!defined('ABSPATH')) {
    15   exit;
     14if ( ! defined( 'ABSPATH' ) ) {
     15    exit;
    1616}
    1717
    1818// プラグインの定数
    19 define('WPBC_PLUGIN_PATH', plugin_dir_path(__FILE__));
     19define( 'WPBC_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
     20
     21/**
     22 * カスタムブロックカテゴリーを追加
     23 */
     24function 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}
     40add_filter( 'block_categories_all', 'wpbc_block_categories', 10, 2 );
    2041
    2142/**
     
    2344 */
    2445function 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}
     52add_action( 'init', 'wpbc_init' );
    3253
    3354/**
     
    3556 */
    3657function 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}
     134add_action( 'rest_api_init', 'wpbc_rest_api_init' );
    98135
    99136/**
    100137 * メタデータ取得のREST APIコールバック
    101138 */
    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   ];
     139function 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    );
    125162}
    126163
     
    128165 * 内部メタデータ取得のREST APIコールバック
    129166 */
    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   ];
     167function 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    );
    147184}
    148185
     
    150187 * URLが内部サイトのURLかどうかを判定する
    151188 */
    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_suffix
    180   ) {
    181     return true;
    182   }
    183 
    184   return false;
     189function 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;
    185222}
    186223
     
    188225 * メタデータを取得する関数
    189226 */
    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;
     227function 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;
    367410}
    368411
     
    370413 * サイト内検索のREST APIコールバック
    371414 */
    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   ];
     415function 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    );
    422465}
    423466
     
    425468 * 内部サイトのメタデータを取得する
    426469 */
    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   ];
     470function 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    );
    455498}
    456499
     
    458501 * キャッシュクリアのREST APIコールバック
    459502 */
    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   ];
     503function 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    );
    494537}
    495538
     
    497540 * ファビコンを取得する関数
    498541 */
    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;
     542function 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;
    543589}
    544590
     
    546592 * 相対URLを絶対URLに変換する
    547593 */
    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, '/');
     594function 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, '/' );
    563609}
    564610
     
    566612 * ファビコンが存在するかチェックする
    567613 */
    568 function wpbc_check_favicon_exists($url) {
    569   $headers = @get_headers($url, 1);
    570   return $headers && strpos($headers[0], '200 OK') !== false;
    571 }
     614function 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  
    55  "version": "2.0.0",
    66  "title": "ブログカード",
    7   "category": "text",
     7  "category": "su-blocks",
    88  "icon": "admin-links",
    99  "description": "URLを入力してブログカードを生成するブロックです。",
  • blogcard-for-wp/trunk/build/index-rtl.css

    r3357123 r3418932  
    1 .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{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-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{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-blogcard){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-blogcard){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  
    55Tested up to: 6.8.2
    66Requires PHP: 7.4
    7 Stable tag: 2.0.2
     7Stable tag: 2.0.6
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    9898== Changelog ==
    9999
     100= 2.0.4 =
     101* Update CSS.
     102
    100103= 2.0.0 =
    101104* Significantly improved block editor UI
Note: See TracChangeset for help on using the changeset viewer.