From b8a465ba0c6724a44db04db8f54ad1eec06b79ba Mon Sep 17 00:00:00 2001 From: Unnati Agrawal Date: Thu, 11 Sep 2025 01:32:01 -0400 Subject: [PATCH] MVP: settings, fetcher, shortcode (TMDB), REST proxy, styles --- assets/styles.css | 6 +++ includes/class-wprc-fetcher.php | 56 ++++++++++++++++++++++ includes/class-wprc-rest.php | 27 +++++++++++ includes/class-wprc-settings.php | 78 +++++++++++++++++++++++++++++++ includes/class-wprc-shortcode.php | 53 +++++++++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 assets/styles.css create mode 100644 includes/class-wprc-fetcher.php create mode 100644 includes/class-wprc-rest.php create mode 100644 includes/class-wprc-settings.php create mode 100644 includes/class-wprc-shortcode.php diff --git a/assets/styles.css b/assets/styles.css new file mode 100644 index 0000000..fd9287f --- /dev/null +++ b/assets/styles.css @@ -0,0 +1,6 @@ +.wprc-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(160px,1fr)); gap:16px; } +.wprc-card { border:1px solid #e5e7eb; border-radius:12px; overflow:hidden; background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.04); } +.wprc-card img { width:100%; height:auto; display:block; } +.wprc-meta { padding:10px; } +.wprc-sub { color:#6b7280; font-size:12px; margin-top:4px; } +.wprc-pre { white-space:pre-wrap; font-size:12px; background:#f8fafc; padding:10px; border-radius:8px; } diff --git a/includes/class-wprc-fetcher.php b/includes/class-wprc-fetcher.php new file mode 100644 index 0000000..a764089 --- /dev/null +++ b/includes/class-wprc-fetcher.php @@ -0,0 +1,56 @@ + 0) { + $cached = get_transient($cache_key); + if ($cached !== false) return $cached; + } + + $headers = ['Accept' => 'application/json']; + if ($token) $headers['Authorization'] = 'Bearer ' . $token; + + $resp = wp_remote_get($url, [ + 'headers' => $headers, + 'timeout' => 20, + ]); + + if (is_wp_error($resp)) { + return ['error' => $resp->get_error_message(), 'status' => 0]; + } + + $code = wp_remote_retrieve_response_code($resp); + $body = wp_remote_retrieve_body($resp); + $data = json_decode($body, true); + + $result = ['status' => $code, 'url' => $url, 'data' => $data]; + if ($cache_minutes > 0 && $code >= 200 && $code < 300) { + set_transient($cache_key, $result, $cache_minutes * MINUTE_IN_SECONDS); + } + return $result; + } +} diff --git a/includes/class-wprc-rest.php b/includes/class-wprc-rest.php new file mode 100644 index 0000000..19a10ea --- /dev/null +++ b/includes/class-wprc-rest.php @@ -0,0 +1,27 @@ + 'GET', + 'callback' => [__CLASS__, 'handle_fetch'], + 'permission_callback' => '__return_true', // read-only, but throttling may be added later + 'args' => [ + 'query' => ['type' => 'string', 'required' => false], + ], + ]); + } + + public static function handle_fetch(\WP_REST_Request $req) { + $query = $req->get_param('query') ?: ''; + $res = Fetcher::fetch(['query' => $query]); + return rest_ensure_response($res); + } +} diff --git a/includes/class-wprc-settings.php b/includes/class-wprc-settings.php new file mode 100644 index 0000000..3c2bca5 --- /dev/null +++ b/includes/class-wprc-settings.php @@ -0,0 +1,78 @@ + [__CLASS__, 'sanitize']]); + + add_settings_section('wprc_main', 'API Settings', function () { + echo '

Set your remote API details. Data is fetched server-side.

'; + }, 'wprc'); + + add_settings_field('base_url', 'Base URL', [__CLASS__, 'field_text'], 'wprc', 'wprc_main', ['key' => 'base_url', 'placeholder' => 'https://api.themoviedb.org/3']); + add_settings_field('endpoint_path', 'Endpoint Path', [__CLASS__, 'field_text'], 'wprc', 'wprc_main', ['key' => 'endpoint_path', 'placeholder' => '/discover/movie']); + add_settings_field('bearer_token', 'Bearer Token', [__CLASS__, 'field_password'], 'wprc', 'wprc_main', ['key' => 'bearer_token']); + add_settings_field('default_query', 'Default Query (key=value&key2=value2)', [__CLASS__, 'field_text'], 'wprc', 'wprc_main', ['key' => 'default_query', 'placeholder' => 'sort_by=popularity.desc']); + add_settings_field('cache_minutes', 'Cache Minutes', [__CLASS__, 'field_number'], 'wprc', 'wprc_main', ['key' => 'cache_minutes', 'placeholder' => '10']); + } + + public static function sanitize($opts) { + $clean = []; + $clean['base_url'] = isset($opts['base_url']) ? esc_url_raw(trim($opts['base_url'])) : ''; + $clean['endpoint_path'] = isset($opts['endpoint_path']) ? sanitize_text_field(trim($opts['endpoint_path'])) : ''; + $clean['bearer_token'] = isset($opts['bearer_token']) ? trim($opts['bearer_token']) : ''; + $clean['default_query'] = isset($opts['default_query']) ? sanitize_text_field(trim($opts['default_query'])) : ''; + $clean['cache_minutes'] = isset($opts['cache_minutes']) ? max(0, intval($opts['cache_minutes'])) : 0; + return $clean; + } + + public static function render_page() { + if (!current_user_can('manage_options')) return; + ?> +
+

WP Remote Content

+
+ +
+

Tip: The token is stored in the database (options table). Don’t commit it to Git.

+
+ ', + esc_attr($type), esc_attr(self::OPTION), $key, esc_attr($val), isset($args['placeholder']) ? esc_attr($args['placeholder']) : '' + ); + } +} diff --git a/includes/class-wprc-shortcode.php b/includes/class-wprc-shortcode.php new file mode 100644 index 0000000..e6b4365 --- /dev/null +++ b/includes/class-wprc-shortcode.php @@ -0,0 +1,53 @@ + '', // e.g. "sort_by=popularity.desc&page=1" + 'type' => 'tmdb', // simple display template + 'limit' => 12 + ], $atts, 'remote_content'); + + $res = Fetcher::fetch(['query' => $atts['query']]); + if (!empty($res['error'])) { + return '

⚠️ Error: ' . esc_html($res['error']) . '

'; + } + if (empty($res['data'])) { + return '

No data.

'; + } + + wp_enqueue_style('wprc-styles'); + + // Very simple renderer for TMDB "results" + $html = '
'; + $items = []; + + if ($atts['type'] === 'tmdb' && isset($res['data']['results']) && is_array($res['data']['results'])) { + $items = array_slice($res['data']['results'], 0, intval($atts['limit'])); + foreach ($items as $m) { + $title = isset($m['title']) ? $m['title'] : (isset($m['name']) ? $m['name'] : 'Untitled'); + $date = isset($m['release_date']) ? $m['release_date'] : ''; + $img = isset($m['poster_path']) ? 'https://image.tmdb.org/t/p/w200' . $m['poster_path'] : ''; + $html .= '
'; + if ($img) $html .= '' . esc_attr($title) . ''; + $html .= '
' . esc_html($title) . ''; + if ($date) $html .= '
' . esc_html($date) . '
'; + $html .= '
'; + } + } else { + // Generic fallback: dump keys for first level arrays + $items = is_array($res['data']) ? $res['data'] : []; + $html .= '
' . esc_html(print_r($items, true)) . '
'; + } + + $html .= '
'; + return $html; + } +}