| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) { |
| 3 |
exit; |
| 4 |
} |
| 5 |
|
| 6 |
$is_activated = presslearn_plugin()->is_plugin_activated(); |
| 7 |
|
| 8 |
if (!$is_activated) { |
| 9 |
wp_redirect(admin_url('admin.php?page=presslearn-settings')); |
| 10 |
exit; |
| 11 |
} |
| 12 |
|
| 13 |
$current_tab = 'settings'; |
| 14 |
if (current_user_can('manage_options') && is_admin() && isset($_GET['tab'])) { |
| 15 |
$tab_value = sanitize_text_field(wp_unslash($_GET['tab'])); |
| 16 |
if (in_array($tab_value, array('settings', 'logs', 'manual'))) { |
| 17 |
$current_tab = $tab_value; |
| 18 |
} |
| 19 |
} |
| 20 |
|
| 21 |
if (isset($_POST['presslearn_toggle_auto_index']) && isset($_POST['enable'])) { |
| 22 |
check_admin_referer('presslearn_toggle_auto_index_nonce'); |
| 23 |
|
| 24 |
if (current_user_can('manage_options')) { |
| 25 |
$enable = sanitize_text_field(wp_unslash($_POST['enable'])); |
| 26 |
if ($enable === 'yes' || $enable === 'no') { |
| 27 |
update_option('presslearn_auto_index_enabled', $enable); |
| 28 |
$updated = true; |
| 29 |
} |
| 30 |
} |
| 31 |
} |
| 32 |
|
| 33 |
if (isset($_POST['save_auto_index_settings'])) { |
| 34 |
check_admin_referer('presslearn_save_auto_index_settings_nonce'); |
| 35 |
|
| 36 |
if (current_user_can('manage_options')) { |
| 37 |
if (isset($_POST['indexnow_api_key'])) { |
| 38 |
$api_key = sanitize_text_field(wp_unslash($_POST['indexnow_api_key'])); |
| 39 |
update_option('presslearn_indexnow_api_key', $api_key); |
| 40 |
|
| 41 |
if (!empty($api_key)) { |
| 42 |
$indexnow = presslearn_indexnow(); |
| 43 |
if ($indexnow->validate_api_key($api_key)) { |
| 44 |
$indexnow->create_api_key_file($api_key); |
| 45 |
} |
| 46 |
} |
| 47 |
} |
| 48 |
|
| 49 |
$auto_indexing_enabled = isset($_POST['auto_indexing_enabled']) ? 'yes' : 'no'; |
| 50 |
update_option('presslearn_auto_indexing_enabled', $auto_indexing_enabled); |
| 51 |
|
| 52 |
$index_post_types = isset($_POST['index_post_types']) && is_array($_POST['index_post_types']) |
| 53 |
? array_map('sanitize_text_field', array_map('wp_unslash', $_POST['index_post_types'])) |
| 54 |
: array(); |
| 55 |
$valid_post_types = array(); |
| 56 |
$allowed_post_types = array('post', 'page'); |
| 57 |
|
| 58 |
foreach ($index_post_types as $post_type) { |
| 59 |
if (in_array($post_type, $allowed_post_types)) { |
| 60 |
$valid_post_types[] = $post_type; |
| 61 |
} |
| 62 |
} |
| 63 |
|
| 64 |
update_option('presslearn_index_post_types', $valid_post_types); |
| 65 |
|
| 66 |
$settings_updated = true; |
| 67 |
} |
| 68 |
} |
| 69 |
|
| 70 |
$auto_index_enabled = get_option('presslearn_auto_index_enabled', 'no'); |
| 71 |
$indexnow_api_key = get_option('presslearn_indexnow_api_key', ''); |
| 72 |
$auto_indexing_enabled = get_option('presslearn_auto_indexing_enabled', 'yes'); |
| 73 |
$index_post_types = get_option('presslearn_index_post_types', array('post', 'page')); |
| 74 |
$has_api_key = !empty($indexnow_api_key); |
| 75 |
|
| 76 |
?> |
| 77 |
|
| 78 |
<div class="presslearn-header"> |
| 79 |
<div class="presslearn-header-logo"> |
| 80 |
<img src="<?php echo esc_url(PRESSLEARN_PLUGIN_URL); ?>assets/images/logo.png" alt="PressLearn Logo"> |
| 81 |
<h1>자동 인덱싱</h1> |
| 82 |
</div> |
| 83 |
<div class="presslearn-header-status"> |
| 84 |
<?php if ($auto_index_enabled === 'yes'): ?> |
| 85 |
<div class="presslearn-header-status-item status-activate"> |
| 86 |
<p>기능이 활성화되었습니다.</p> |
| 87 |
</div> |
| 88 |
<?php else: ?> |
| 89 |
<div class="presslearn-header-status-item status-deactivate"> |
| 90 |
<p>기능이 비활성화 상태� |
| 91 |
니다.</p> |
| 92 |
</div> |
| 93 |
<?php endif; ?> |
| 94 |
</div> |
| 95 |
</div> |
| 96 |
|
| 97 |
<div class="wrap"> |
| 98 |
|
| 99 |
<div class="presslearn-breadcrumbs-wrap"> |
| 100 |
<div class="presslearn-breadcrumbs"> |
| 101 |
<span>대시보드</span> |
| 102 |
<span class="divider">/</span> |
| 103 |
<span class="active">자동 인덱싱</span> |
| 104 |
</div> |
| 105 |
</div> |
| 106 |
|
| 107 |
<?php if (isset($settings_updated) && $settings_updated): ?> |
| 108 |
<div class="notice notice-success inline"> |
| 109 |
<p>설정 정보가 성공적으로 저장되었습니다.</p> |
| 110 |
</div> |
| 111 |
<?php endif; ?> |
| 112 |
|
| 113 |
<?php if (isset($updated) && $updated): ?> |
| 114 |
<div class="notice notice-success inline"> |
| 115 |
<p>자동 인덱싱 기능 설정이 변경되었습니다.</p> |
| 116 |
</div> |
| 117 |
<?php endif; ?> |
| 118 |
|
| 119 |
<?php if (!$has_api_key && $auto_index_enabled === 'yes'): ?> |
| 120 |
<div class="notice notice-warning inline"> |
| 121 |
<p>IndexNow API 키를 설정해야 인덱싱 기능을 사용할 수 있습니다.<br/><a href="https://www.bing.com/indexnow" target="_blank">IndexNow API 키 발급 받기</a></p> |
| 122 |
</div> |
| 123 |
<?php endif; ?> |
| 124 |
|
| 125 |
<div class="presslearn-tabs"> |
| 126 |
<a href="?page=presslearn-auto-index&tab=settings" class="tab-link <?php echo esc_attr($current_tab === 'settings' ? 'active' : ''); ?>">설정</a> |
| 127 |
<a href="?page=presslearn-auto-index&tab=logs" class="tab-link <?php echo esc_attr($current_tab === 'logs' ? 'active' : ''); ?>">로그</a> |
| 128 |
<a href="?page=presslearn-auto-index&tab=manual" class="tab-link <?php echo esc_attr($current_tab === 'manual' ? 'active' : ''); ?>">수동 인덱싱</a> |
| 129 |
</div> |
| 130 |
|
| 131 |
<?php if ($current_tab === 'settings'): ?> |
| 132 |
|
| 133 |
<div class="presslearn-card"> |
| 134 |
<div class="presslearn-card-header"> |
| 135 |
<h2>자동 인덱싱</h2> |
| 136 |
<p>IndexNow 프로토콜을 사용하여 네이버에 자동으로 인덱싱 요청을 보내는 기능� |
| 137 |
니다.</p> |
| 138 |
</div> |
| 139 |
<div class="presslearn-card-body"> |
| 140 |
<div class="presslearn-card-body-row"> |
| 141 |
<div class="presslearn-card-body-row-item grid-left"> |
| 142 |
<h3>플러그인 기능 활성화</h3> |
| 143 |
</div> |
| 144 |
<div class="presslearn-card-body-row-item grid-right"> |
| 145 |
<form method="post" action="" id="auto-index-toggle-form"> |
| 146 |
<?php wp_nonce_field('presslearn_toggle_auto_index_nonce'); ?> |
| 147 |
<input type="hidden" name="presslearn_toggle_auto_index" value="1"> |
| 148 |
<input type="hidden" name="enable" id="auto-index-enable-value" value="<?php echo esc_attr($auto_index_enabled === 'yes' ? 'no' : 'yes'); ?>"> |
| 149 |
|
| 150 |
<label class="switch"> |
| 151 |
<input type="checkbox" <?php echo esc_attr($auto_index_enabled === 'yes' ? 'checked' : ''); ?> onchange="document.getElementById('auto-index-enable-value').value = this.checked ? 'yes' : 'no'; document.getElementById('auto-index-toggle-form').submit();"> |
| 152 |
<span class="slider round"></span> |
| 153 |
</label> |
| 154 |
</form> |
| 155 |
</div> |
| 156 |
</div> |
| 157 |
<form method="post" action="" id="auto-index-settings-form"> |
| 158 |
<?php wp_nonce_field('presslearn_save_auto_index_settings_nonce'); ?> |
| 159 |
<input type="hidden" name="save_auto_index_settings" value="1"> |
| 160 |
|
| 161 |
<div class="presslearn-card-body-row"> |
| 162 |
<div class="presslearn-card-body-row-item grid-left"> |
| 163 |
<h3>IndexNow API 키</h3> |
| 164 |
</div> |
| 165 |
<div class="presslearn-card-body-row-item grid-right"> |
| 166 |
<input type="text" name="indexnow_api_key" class="regular-text" value="<?php echo esc_attr($indexnow_api_key); ?>" placeholder="32자리 영숫자 API 키를 � |
| 167 |
력하세요"> |
| 168 |
</div> |
| 169 |
</div> |
| 170 |
|
| 171 |
<div class="presslearn-card-body-row"> |
| 172 |
<div class="presslearn-card-body-row-item grid-left"> |
| 173 |
<h3>자동 인덱싱 활성화</h3> |
| 174 |
<p style="font-size: 13px; color: #666; margin-top: 5px;">게시글 발행/수정 시 자동으로 인덱싱 요청</p> |
| 175 |
</div> |
| 176 |
<div class="presslearn-card-body-row-item grid-right"> |
| 177 |
<label class="switch"> |
| 178 |
<input type="checkbox" name="auto_indexing_enabled" <?php echo esc_attr($auto_indexing_enabled === 'yes' ? 'checked' : ''); ?>> |
| 179 |
<span class="slider round"></span> |
| 180 |
</label> |
| 181 |
</div> |
| 182 |
</div> |
| 183 |
|
| 184 |
<div class="presslearn-card-body-row"> |
| 185 |
<div class="presslearn-card-body-row-item grid-left"> |
| 186 |
<h3>인덱싱 대상 콘� |
| 187 |
�츠</h3> |
| 188 |
</div> |
| 189 |
<div class="presslearn-card-body-row-item grid-right"> |
| 190 |
<div class="social-share-options"> |
| 191 |
<label class="social-share-button"> |
| 192 |
<input type="checkbox" name="index_post_types[]" value="post" <?php echo esc_attr(in_array('post', $index_post_types) ? 'checked' : ''); ?>> |
| 193 |
<span class="social-btn">게시글</span> |
| 194 |
</label><label class="social-share-button"> |
| 195 |
<input type="checkbox" name="index_post_types[]" value="page" <?php echo esc_attr(in_array('page', $index_post_types) ? 'checked' : ''); ?>> |
| 196 |
<span class="social-btn">페이지</span> |
| 197 |
</label> |
| 198 |
</div> |
| 199 |
</div> |
| 200 |
</div> |
| 201 |
</form> |
| 202 |
</div> |
| 203 |
<div class="presslearn-card-footer"> |
| 204 |
<div class="presslearn-card-footer-item grid-left"> |
| 205 |
<span class="tip_button">IndexNow는 요청을 보낼 뿐, 인덱싱 결과를 보장하지 않습니다.</span> |
| 206 |
</div> |
| 207 |
<div class="presslearn-card-footer-item grid-right"> |
| 208 |
<button type="submit" class="point-btn" form="auto-index-settings-form">저장하기</button> |
| 209 |
</div> |
| 210 |
</div> |
| 211 |
</div> |
| 212 |
|
| 213 |
<?php elseif ($current_tab === 'logs'): ?> |
| 214 |
|
| 215 |
<div class="presslearn-card"> |
| 216 |
<div class="presslearn-card-header"> |
| 217 |
<h2>인덱싱 로그</h2> |
| 218 |
<p>인덱싱 요청 결과와 상태를 확인할 수 있습니다.</p> |
| 219 |
</div> |
| 220 |
<div class="presslearn-card-body"> |
| 221 |
<div class="presslearn-card-body-row"> |
| 222 |
<div class="presslearn-card-body-row-item" style="flex: 100%;"> |
| 223 |
<?php |
| 224 |
global $wpdb; |
| 225 |
$table_name = $wpdb->prefix . 'presslearn_indexing_logs'; |
| 226 |
$logs_per_page = 20; |
| 227 |
$current_page = isset($_GET['logs_page']) ? max(1, intval($_GET['logs_page'])) : 1; |
| 228 |
$logs_search = isset($_GET['logs_search']) ? sanitize_text_field($_GET['logs_search']) : ''; |
| 229 |
$offset = ($current_page - 1) * $logs_per_page; |
| 230 |
|
| 231 |
$logs = array(); |
| 232 |
$total_logs = 0; |
| 233 |
|
| 234 |
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name) { |
| 235 |
$where_clause = ''; |
| 236 |
$params = array(); |
| 237 |
|
| 238 |
if (!empty($logs_search)) { |
| 239 |
$where_clause = "WHERE post_title LIKE %s OR post_url LIKE %s"; |
| 240 |
$search_term = '%' . $wpdb->esc_like($logs_search) . '%'; |
| 241 |
$params[] = $search_term; |
| 242 |
$params[] = $search_term; |
| 243 |
} |
| 244 |
|
| 245 |
if (!empty($logs_search)) { |
| 246 |
$total_logs = $wpdb->get_var($wpdb->prepare( |
| 247 |
"SELECT COUNT(*) FROM $table_name $where_clause", |
| 248 |
$params |
| 249 |
)); |
| 250 |
} else { |
| 251 |
$total_logs = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); |
| 252 |
} |
| 253 |
|
| 254 |
if (!empty($logs_search)) { |
| 255 |
$logs = $wpdb->get_results($wpdb->prepare( |
| 256 |
"SELECT * FROM $table_name $where_clause ORDER BY indexed_at DESC LIMIT %d OFFSET %d", |
| 257 |
array_merge($params, array($logs_per_page, $offset)) |
| 258 |
), ARRAY_A); |
| 259 |
} else { |
| 260 |
$logs = $wpdb->get_results($wpdb->prepare( |
| 261 |
"SELECT * FROM $table_name ORDER BY indexed_at DESC LIMIT %d OFFSET %d", |
| 262 |
$logs_per_page, |
| 263 |
$offset |
| 264 |
), ARRAY_A); |
| 265 |
} |
| 266 |
} |
| 267 |
|
| 268 |
$max_pages = ceil($total_logs / $logs_per_page); |
| 269 |
?> |
| 270 |
|
| 271 |
<form method="get" action="" style="margin-bottom: 15px;"> |
| 272 |
<input type="hidden" name="page" value="presslearn-auto-index"> |
| 273 |
<input type="hidden" name="tab" value="logs"> |
| 274 |
<div style="display: flex; align-items: center; gap: 10px;"> |
| 275 |
<input type="text" name="logs_search" class="regular-text" placeholder="게시글 제목 또는 URL 검색" style="width: 300px;" value="<?php echo esc_attr($logs_search); ?>"> |
| 276 |
<button type="submit" class="secondary-btn">검색</button> |
| 277 |
<?php if (!empty($logs_search)): ?> |
| 278 |
<a href="<?php echo esc_url(add_query_arg(array('page' => 'presslearn-auto-index', 'tab' => 'logs'))); ?>" class="button">전체 보기</a> |
| 279 |
<?php endif; ?> |
| 280 |
</div> |
| 281 |
</form> |
| 282 |
|
| 283 |
<?php if (empty($logs)): ?> |
| 284 |
<?php if (!empty($logs_search)): ?> |
| 285 |
<p style="text-align: center; color: #666; padding: 40px;">'<?php echo esc_html($logs_search); ?>' 검색 결과가 없습니다.</p> |
| 286 |
<?php else: ?> |
| 287 |
<p style="text-align: center; color: #666; padding: 40px;">아직 인덱싱 로그가 없습니다.</p> |
| 288 |
<?php endif; ?> |
| 289 |
<?php else: ?> |
| 290 |
<table class="widefat"> |
| 291 |
<thead> |
| 292 |
<tr> |
| 293 |
<th>게시글</th> |
| 294 |
<th>요청 유형</th> |
| 295 |
<th>응답 코드</th> |
| 296 |
<th>응답 메시지</th> |
| 297 |
<th>요청 시간</th> |
| 298 |
</tr> |
| 299 |
</thead> |
| 300 |
<tbody> |
| 301 |
<?php foreach ($logs as $log): ?> |
| 302 |
<tr> |
| 303 |
<td> |
| 304 |
<strong><?php echo esc_html($log['post_title']); ?></strong><br> |
| 305 |
<small><a href="<?php echo esc_url($log['post_url']); ?>" target="_blank"><?php echo esc_html($log['post_url']); ?></a></small> |
| 306 |
</td> |
| 307 |
<td> |
| 308 |
<?php if ($log['request_type'] === 'auto'): ?> |
| 309 |
<span style="background: #E3F2FD; color: #1976D2; padding: 2px 8px; border-radius: 12px; font-size: 11px;">자동</span> |
| 310 |
<?php elseif ($log['request_type'] === 'manual'): ?> |
| 311 |
<span style="background: #E8F5E9; color: #388E3C; padding: 2px 8px; border-radius: 12px; font-size: 11px;">수동</span> |
| 312 |
<?php else: ?> |
| 313 |
<span style="background: #FFF3E0; color: #F57C00; padding: 2px 8px; border-radius: 12px; font-size: 11px;">일괄</span> |
| 314 |
<?php endif; ?> |
| 315 |
</td> |
| 316 |
<td> |
| 317 |
<?php |
| 318 |
$is_success = in_array($log['response_code'], array('200', '202')); |
| 319 |
$color = $is_success ? '#4CAF50' : '#f44336'; |
| 320 |
?> |
| 321 |
<span style="color: <?php echo esc_attr($color); ?>; font-weight: bold;"><?php echo esc_html($log['response_code'] ?: 'N/A'); ?></span> |
| 322 |
</td> |
| 323 |
<td><?php echo esc_html($log['response_message'] ?: ''); ?></td> |
| 324 |
<td><?php echo esc_html(mysql2date('Y-m-d H:i:s', $log['indexed_at'])); ?></td> |
| 325 |
</tr> |
| 326 |
<?php endforeach; ?> |
| 327 |
</tbody> |
| 328 |
</table> |
| 329 |
|
| 330 |
<?php if ($max_pages > 1): ?> |
| 331 |
<div class="tablenav"> |
| 332 |
<div class="tablenav-pages"> |
| 333 |
<span class="displaying-num">총 <?php echo esc_html(number_format($total_logs)); ?>개 항목</span> |
| 334 |
<span class="pagination-links"> |
| 335 |
<?php |
| 336 |
$logs_base_url = add_query_arg(array( |
| 337 |
'page' => 'presslearn-auto-index', |
| 338 |
'tab' => 'logs', |
| 339 |
'logs_search' => $logs_search |
| 340 |
)); |
| 341 |
?> |
| 342 |
<?php if ($current_page > 1): ?> |
| 343 |
<a href="<?php echo esc_url(add_query_arg('logs_page', 1, $logs_base_url)); ?>" class="button">처음</a> |
| 344 |
<a href="<?php echo esc_url(add_query_arg('logs_page', $current_page - 1, $logs_base_url)); ?>" class="button">이전</a> |
| 345 |
<?php endif; ?> |
| 346 |
|
| 347 |
<span class="paging-input"><?php echo esc_html($current_page); ?> / <?php echo esc_html($max_pages); ?></span> |
| 348 |
|
| 349 |
<?php if ($current_page < $max_pages): ?> |
| 350 |
<a href="<?php echo esc_url(add_query_arg('logs_page', $current_page + 1, $logs_base_url)); ?>" class="button">다음</a> |
| 351 |
<a href="<?php echo esc_url(add_query_arg('logs_page', $max_pages, $logs_base_url)); ?>" class="button">마지막</a> |
| 352 |
<?php endif; ?> |
| 353 |
</span> |
| 354 |
</div> |
| 355 |
</div> |
| 356 |
<?php endif; ?> |
| 357 |
<?php endif; ?> |
| 358 |
</div> |
| 359 |
</div> |
| 360 |
</div> |
| 361 |
<div class="presslearn-card-footer"> |
| 362 |
<div class="presslearn-card-footer-item grid-left"> |
| 363 |
<span class="tip_button">응답 코드 200, 202는 성공을 의미하며, 실제 인덱싱은 검색 엔진의 판단에 따라 결정됩니다.</span> |
| 364 |
</div> |
| 365 |
<div class="presslearn-card-footer-item grid-right"> |
| 366 |
</div> |
| 367 |
</div> |
| 368 |
</div> |
| 369 |
|
| 370 |
<?php elseif ($current_tab === 'manual'): ?> |
| 371 |
|
| 372 |
<div class="presslearn-card"> |
| 373 |
<div class="presslearn-card-header"> |
| 374 |
<h2>수동 인덱싱</h2> |
| 375 |
<p>게시글을 수동으로 선택하여 인덱싱 요청을 보낼 수 있습니다. 인덱싱 상태는 가장 최근 요청 결과를 기준으로 표시됩니다.</p> |
| 376 |
</div> |
| 377 |
<div class="presslearn-card-body"> |
| 378 |
<?php if (!$has_api_key): ?> |
| 379 |
<div class="notice notice-error inline"> |
| 380 |
<p>수동 인덱싱을 사용하려면 먼저 IndexNow API 키를 설정해주세요.</p> |
| 381 |
</div> |
| 382 |
<?php else: ?> |
| 383 |
<div class="presslearn-card-body-row"> |
| 384 |
<div class="presslearn-card-body-row-item" style="flex: 100%;"> |
| 385 |
<?php |
| 386 |
$posts_per_page = 20; |
| 387 |
$current_page = isset($_GET['posts_page']) ? max(1, intval($_GET['posts_page'])) : 1; |
| 388 |
$search_term = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : ''; |
| 389 |
|
| 390 |
$args = array( |
| 391 |
'post_type' => array('post', 'page'), |
| 392 |
'post_status' => 'publish', |
| 393 |
'posts_per_page' => $posts_per_page, |
| 394 |
'paged' => $current_page, |
| 395 |
'meta_query' => array( |
| 396 |
'relation' => 'OR', |
| 397 |
array( |
| 398 |
'key' => '_presslearn_exclude_from_index', |
| 399 |
'value' => 'yes', |
| 400 |
'compare' => '!=' |
| 401 |
), |
| 402 |
array( |
| 403 |
'key' => '_presslearn_exclude_from_index', |
| 404 |
'compare' => 'NOT EXISTS' |
| 405 |
) |
| 406 |
) |
| 407 |
); |
| 408 |
|
| 409 |
if (!empty($search_term)) { |
| 410 |
$args['s'] = $search_term; |
| 411 |
} |
| 412 |
|
| 413 |
$query = new WP_Query($args); |
| 414 |
|
| 415 |
function get_post_indexing_status($post_id) { |
| 416 |
global $wpdb; |
| 417 |
$table_name = $wpdb->prefix . 'presslearn_indexing_logs'; |
| 418 |
|
| 419 |
if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) { |
| 420 |
return array( |
| 421 |
'status' => 'never', |
| 422 |
'label' => '미요청', |
| 423 |
'class' => 'never', |
| 424 |
'last_indexed' => null |
| 425 |
); |
| 426 |
} |
| 427 |
|
| 428 |
$recent_log = $wpdb->get_row($wpdb->prepare( |
| 429 |
"SELECT response_code, indexed_at, request_type |
| 430 |
FROM $table_name |
| 431 |
WHERE post_id = %d |
| 432 |
ORDER BY indexed_at DESC |
| 433 |
LIMIT 1", |
| 434 |
$post_id |
| 435 |
)); |
| 436 |
|
| 437 |
if (!$recent_log) { |
| 438 |
return array( |
| 439 |
'status' => 'never', |
| 440 |
'label' => '미요청', |
| 441 |
'class' => 'never', |
| 442 |
'last_indexed' => null |
| 443 |
); |
| 444 |
} |
| 445 |
|
| 446 |
$success_codes = array('200', '202'); |
| 447 |
$is_success = in_array($recent_log->response_code, $success_codes); |
| 448 |
|
| 449 |
if ($is_success) { |
| 450 |
return array( |
| 451 |
'status' => 'success', |
| 452 |
'label' => '성공', |
| 453 |
'class' => 'success', |
| 454 |
'last_indexed' => $recent_log->indexed_at |
| 455 |
); |
| 456 |
} else { |
| 457 |
return array( |
| 458 |
'status' => 'failed', |
| 459 |
'label' => '실패', |
| 460 |
'class' => 'failed', |
| 461 |
'last_indexed' => $recent_log->indexed_at |
| 462 |
); |
| 463 |
} |
| 464 |
} |
| 465 |
?> |
| 466 |
|
| 467 |
<form method="get" action="" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;"> |
| 468 |
<input type="hidden" name="page" value="presslearn-auto-index"> |
| 469 |
<input type="hidden" name="tab" value="manual"> |
| 470 |
<div style="display: flex; align-items: center; gap: 10px;"> |
| 471 |
<input type="text" name="search" class="regular-text" placeholder="게시글 제목 검색" style="width: 250px;" value="<?php echo esc_attr($search_term); ?>"> |
| 472 |
<button type="submit" class="secondary-btn">검색</button> |
| 473 |
</div> |
| 474 |
<div> |
| 475 |
<button type="button" class="point-btn" id="bulk-index-btn" disabled>선택된 게시글 인덱싱</button> |
| 476 |
</div> |
| 477 |
</form> |
| 478 |
|
| 479 |
<?php if (!$query->have_posts()): ?> |
| 480 |
<p style="text-align: center; color: #666; padding: 40px;">인덱싱할 게시글이 없습니다.</p> |
| 481 |
<?php else: ?> |
| 482 |
<form id="bulk-index-form"> |
| 483 |
<?php wp_nonce_field('presslearn_indexing_nonce', 'bulk_index_nonce'); ?> |
| 484 |
<table class="widefat"> |
| 485 |
<thead> |
| 486 |
<tr> |
| 487 |
<th style="width: 40px;"><input type="checkbox" id="select-all-posts"></th> |
| 488 |
<th>제목</th> |
| 489 |
<th>유형</th> |
| 490 |
<th>작성일</th> |
| 491 |
<th>인덱싱 상태</th> |
| 492 |
<th>동작</th> |
| 493 |
</tr> |
| 494 |
</thead> |
| 495 |
<tbody> |
| 496 |
<?php while ($query->have_posts()): $query->the_post(); ?> |
| 497 |
<?php |
| 498 |
$post_id = get_the_ID(); |
| 499 |
$indexing_status = get_post_indexing_status($post_id); |
| 500 |
?> |
| 501 |
<tr> |
| 502 |
<td><input type="checkbox" class="post-checkbox" name="post_ids[]" value="<?php echo esc_attr($post_id); ?>"></td> |
| 503 |
<td> |
| 504 |
<strong><?php echo esc_html(get_the_title()); ?></strong><br> |
| 505 |
<small><a href="<?php echo esc_url(get_permalink()); ?>" target="_blank"><?php echo esc_html(get_permalink()); ?></a></small> |
| 506 |
</td> |
| 507 |
<td> |
| 508 |
<?php if (get_post_type() === 'post'): ?> |
| 509 |
<span style="background: #E3F2FD; color: #1976D2; padding: 2px 8px; border-radius: 12px; font-size: 11px;">게시글</span> |
| 510 |
<?php else: ?> |
| 511 |
<span style="background: #E8F5E9; color: #388E3C; padding: 2px 8px; border-radius: 12px; font-size: 11px;">페이지</span> |
| 512 |
<?php endif; ?> |
| 513 |
</td> |
| 514 |
<td><?php echo esc_html(get_the_date('Y-m-d H:i:s')); ?></td> |
| 515 |
<td> |
| 516 |
<?php |
| 517 |
if ($indexing_status['class'] === 'success') { |
| 518 |
$style = 'background: #E8F5E9; color: #2E7D32; padding: 3px 8px; border-radius: 12px; font-size: 11px; font-weight: 500;'; |
| 519 |
} elseif ($indexing_status['class'] === 'failed') { |
| 520 |
$style = 'background: #FFEBEE; color: #C62828; padding: 3px 8px; border-radius: 12px; font-size: 11px; font-weight: 500;'; |
| 521 |
} else { |
| 522 |
$style = 'background: #F5F5F5; color: #666; padding: 3px 8px; border-radius: 12px; font-size: 11px; font-weight: 500;'; |
| 523 |
} |
| 524 |
$tooltip = $indexing_status['last_indexed'] |
| 525 |
? '마지막 요청: ' . mysql2date('Y-m-d H:i:s', $indexing_status['last_indexed']) |
| 526 |
: '아직 인덱싱 요청을 하지 않았습니다'; |
| 527 |
?> |
| 528 |
<span style="<?php echo esc_attr($style); ?>" title="<?php echo esc_attr($tooltip); ?>"><?php echo esc_html($indexing_status['label']); ?></span> |
| 529 |
</td> |
| 530 |
<td> |
| 531 |
<button type="button" class="point-btn index-single-btn" data-post-id="<?php echo esc_attr($post_id); ?>">인덱싱</button> |
| 532 |
</td> |
| 533 |
</tr> |
| 534 |
<?php endwhile; ?> |
| 535 |
</tbody> |
| 536 |
</table> |
| 537 |
</form> |
| 538 |
|
| 539 |
<?php if ($query->max_num_pages > 1): ?> |
| 540 |
<div class="tablenav"> |
| 541 |
<div class="tablenav-pages"> |
| 542 |
<span class="displaying-num">총 <?php echo esc_html(number_format($query->found_posts)); ?>개 항목</span> |
| 543 |
<span class="pagination-links"> |
| 544 |
<?php |
| 545 |
$base_url = add_query_arg(array( |
| 546 |
'page' => 'presslearn-auto-index', |
| 547 |
'tab' => 'manual', |
| 548 |
'search' => $search_term |
| 549 |
)); |
| 550 |
?> |
| 551 |
<?php if ($current_page > 1): ?> |
| 552 |
<a href="<?php echo esc_url(add_query_arg('posts_page', 1, $base_url)); ?>" class="button">처음</a> |
| 553 |
<a href="<?php echo esc_url(add_query_arg('posts_page', $current_page - 1, $base_url)); ?>" class="button">이전</a> |
| 554 |
<?php endif; ?> |
| 555 |
|
| 556 |
<span class="paging-input"><?php echo esc_html($current_page); ?> / <?php echo esc_html($query->max_num_pages); ?></span> |
| 557 |
|
| 558 |
<?php if ($current_page < $query->max_num_pages): ?> |
| 559 |
<a href="<?php echo esc_url(add_query_arg('posts_page', $current_page + 1, $base_url)); ?>" class="button">다음</a> |
| 560 |
<a href="<?php echo esc_url(add_query_arg('posts_page', $query->max_num_pages, $base_url)); ?>" class="button">마지막</a> |
| 561 |
<?php endif; ?> |
| 562 |
</span> |
| 563 |
</div> |
| 564 |
</div> |
| 565 |
<?php endif; ?> |
| 566 |
<?php endif; ?> |
| 567 |
<?php wp_reset_postdata(); ?> |
| 568 |
</div> |
| 569 |
</div> |
| 570 |
<?php endif; ?> |
| 571 |
</div> |
| 572 |
<div class="presslearn-card-footer"> |
| 573 |
<div class="presslearn-card-footer-item grid-left"> |
| 574 |
<span class="tip_button">한 번에 최대 50개까지 선택하여 일괄 인덱싱할 수 있습니다.</span> |
| 575 |
</div> |
| 576 |
<div class="presslearn-card-footer-item grid-right"> |
| 577 |
</div> |
| 578 |
</div> |
| 579 |
</div> |
| 580 |
|
| 581 |
<?php endif; ?> |
| 582 |
</div> |
| 583 |
|
| 584 |
<script type="text/javascript"> |
| 585 |
document.addEventListener('DOMContentLoaded', function() { |
| 586 |
const selectAllCheckbox = document.getElementById('select-all-posts'); |
| 587 |
if (selectAllCheckbox) { |
| 588 |
selectAllCheckbox.addEventListener('change', function() { |
| 589 |
const checkboxes = document.querySelectorAll('.post-checkbox'); |
| 590 |
checkboxes.forEach(function(checkbox) { |
| 591 |
checkbox.checked = selectAllCheckbox.checked; |
| 592 |
}); |
| 593 |
updateBulkIndexButton(); |
| 594 |
}); |
| 595 |
} |
| 596 |
|
| 597 |
const postCheckboxes = document.querySelectorAll('.post-checkbox'); |
| 598 |
postCheckboxes.forEach(function(checkbox) { |
| 599 |
checkbox.addEventListener('change', updateBulkIndexButton); |
| 600 |
}); |
| 601 |
|
| 602 |
const indexButtons = document.querySelectorAll('.index-single-btn'); |
| 603 |
indexButtons.forEach(function(button) { |
| 604 |
button.addEventListener('click', function() { |
| 605 |
const postId = this.getAttribute('data-post-id'); |
| 606 |
indexSinglePost(postId, this); |
| 607 |
}); |
| 608 |
}); |
| 609 |
|
| 610 |
function updateBulkIndexButton() { |
| 611 |
const checkedBoxes = document.querySelectorAll('.post-checkbox:checked'); |
| 612 |
const bulkButton = document.getElementById('bulk-index-btn'); |
| 613 |
if (bulkButton) { |
| 614 |
bulkButton.disabled = checkedBoxes.length === 0; |
| 615 |
bulkButton.textContent = checkedBoxes.length > 0 ? '선택된 ' + checkedBoxes.length + '개 게시글 인덱싱' : '선택된 게시글 인덱싱'; |
| 616 |
} |
| 617 |
} |
| 618 |
|
| 619 |
function indexSinglePost(postId, button) { |
| 620 |
const originalText = button.textContent; |
| 621 |
button.textContent = '처리중'; |
| 622 |
button.disabled = true; |
| 623 |
|
| 624 |
const formData = new FormData(); |
| 625 |
formData.append('action', 'presslearn_manual_index'); |
| 626 |
formData.append('nonce', '<?php echo esc_js(wp_create_nonce('presslearn_indexing_nonce')); ?>'); |
| 627 |
formData.append('post_id', postId); |
| 628 |
|
| 629 |
fetch('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', { |
| 630 |
method: 'POST', |
| 631 |
credentials: 'same-origin', |
| 632 |
body: formData |
| 633 |
}) |
| 634 |
.then(response => response.json()) |
| 635 |
.then(data => { |
| 636 |
if (data.success) { |
| 637 |
alert('인덱싱 요청이 성공적으로 전송되었습니다.'); |
| 638 |
location.reload(); |
| 639 |
} else { |
| 640 |
alert('인덱싱 요청 실패: ' + (data.data.message || '알 수 없는 오류')); |
| 641 |
} |
| 642 |
}) |
| 643 |
.catch(error => { |
| 644 |
console.error('Error:', error); |
| 645 |
alert('인덱싱 요청 중 오류가 발생했습니다.'); |
| 646 |
}) |
| 647 |
.finally(function() { |
| 648 |
button.textContent = originalText; |
| 649 |
button.disabled = false; |
| 650 |
}); |
| 651 |
} |
| 652 |
|
| 653 |
const bulkIndexButton = document.getElementById('bulk-index-btn'); |
| 654 |
if (bulkIndexButton) { |
| 655 |
bulkIndexButton.addEventListener('click', function() { |
| 656 |
const checkedBoxes = document.querySelectorAll('.post-checkbox:checked'); |
| 657 |
if (checkedBoxes.length === 0) { |
| 658 |
alert('인덱싱할 게시글을 선택해주세요.'); |
| 659 |
return; |
| 660 |
} |
| 661 |
|
| 662 |
if (checkedBoxes.length > 50) { |
| 663 |
alert('한 번에 최대 50개까지만 선택할 수 있습니다.'); |
| 664 |
return; |
| 665 |
} |
| 666 |
|
| 667 |
if (!confirm(checkedBoxes.length + '개의 게시글을 인덱싱하시겠습니까?')) { |
| 668 |
return; |
| 669 |
} |
| 670 |
|
| 671 |
const postIds = Array.from(checkedBoxes).map(function(checkbox) { |
| 672 |
return checkbox.value; |
| 673 |
}); |
| 674 |
|
| 675 |
const originalText = this.textContent; |
| 676 |
this.textContent = '처리중'; |
| 677 |
this.disabled = true; |
| 678 |
|
| 679 |
const formData = new FormData(); |
| 680 |
formData.append('action', 'presslearn_bulk_index'); |
| 681 |
formData.append('nonce', '<?php echo esc_js(wp_create_nonce('presslearn_indexing_nonce')); ?>'); |
| 682 |
postIds.forEach(function(postId) { |
| 683 |
formData.append('post_ids[]', postId); |
| 684 |
}); |
| 685 |
|
| 686 |
fetch('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', { |
| 687 |
method: 'POST', |
| 688 |
credentials: 'same-origin', |
| 689 |
body: formData |
| 690 |
}) |
| 691 |
.then(response => response.json()) |
| 692 |
.then(data => { |
| 693 |
if (data.success) { |
| 694 |
alert(data.data.message); |
| 695 |
location.reload(); |
| 696 |
} else { |
| 697 |
alert('일괄 인덱싱 실패: ' + (data.data.message || '알 수 없는 오류')); |
| 698 |
} |
| 699 |
}) |
| 700 |
.catch(error => { |
| 701 |
console.error('Error:', error); |
| 702 |
alert('일괄 인덱싱 중 오류가 발생했습니다.'); |
| 703 |
}) |
| 704 |
.finally(() => { |
| 705 |
this.textContent = originalText; |
| 706 |
this.disabled = false; |
| 707 |
}); |
| 708 |
}); |
| 709 |
} |
| 710 |
|
| 711 |
updateBulkIndexButton(); |
| 712 |
}); |
| 713 |
</script> |
| 714 |
|
| 715 |
|