PluginProbe
AL Pack / 1.2.0
AL Pack v1.2.0
1.3.13 trunk 1.0.0 1.1.1 1.1.2 1.2.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.11 1.3.12
alpack / presslearn-plugin.php

presslearn-plugin.php in AL Pack 1.2.0, at presslearn-plugin.php

4,555 lines 160.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: AL Pack - 워드프레스를 위한 통계, 광고 도구
4 * Plugin URI: https://alpack.dev
5 * Description: 통계, 글쓰기 SEO, 무효 트래픽 차단, 빠른 버튼 생성, 카카오 공유 버튼, 스크롤 팝�
6 , 애드클리커 등 워드프레스를 위한 통합 플러그인
7 * Version: 1.2.0
8 * Author: 프레스런
9 * Author URI: https://alpack.dev
10 * Text Domain: alpack
11 * License: GPLv2 or later
12 */
13
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 define('PRESSLEARN_PLUGIN_DIR', plugin_dir_path(__FILE__));
19 define('PRESSLEARN_PLUGIN_URL', plugin_dir_url(__FILE__));
20 define('PRESSLEARN_PLUGIN_VERSION', time());
21
22 register_activation_hook(__FILE__, 'presslearn_plugin_activate');
23 register_deactivation_hook(__FILE__, 'presslearn_plugin_deactivate');
24 register_uninstall_hook(__FILE__, 'presslearn_plugin_uninstall');
25
26 function presslearn_plugin_activate() {
27 $is_first_install = empty(get_option('presslearn_plugin_header_version', ''));
28
29 update_option('presslearn_plugin_key', '');
30 update_option('presslearn_plugin_activated_time', time());
31 update_option('presslearn_activation_logs', array());
32 update_option('presslearn_plugin_settings', array(
33 'initialized' => true,
34 'version' => PRESSLEARN_PLUGIN_VERSION,
35 'activation_date' => current_time('mysql')
36 ));
37
38 if ($is_first_install) {
39 update_option('presslearn_scroll_depth_enabled', 'no');
40 update_option('presslearn_analytics_enabled', 'no');
41 update_option('presslearn_dynamic_banner_enabled', 'no');
42 update_option('presslearn_click_protection_enabled', 'no');
43 update_option('presslearn_ad_clicker_enabled', 'no');
44 update_option('presslearn_social_share_enabled', 'no');
45 add_option('presslearn_quick_button_enabled', 'no');
46 add_option('presslearn_button_transition_enabled', 'no');
47 add_option('presslearn_auto_index_enabled', 'no');
48 add_option('presslearn_header_footer_enabled', 'no');
49 } else {
50 add_option('presslearn_scroll_depth_enabled', 'no');
51 add_option('presslearn_analytics_enabled', 'no');
52 add_option('presslearn_dynamic_banner_enabled', 'no');
53 add_option('presslearn_click_protection_enabled', 'no');
54 add_option('presslearn_ad_clicker_enabled', 'no');
55 add_option('presslearn_social_share_enabled', 'no');
56 add_option('presslearn_quick_button_enabled', 'no');
57 add_option('presslearn_button_transition_enabled', 'no');
58 add_option('presslearn_auto_index_enabled', 'no');
59 add_option('presslearn_header_footer_enabled', 'no');
60 }
61 presslearn_create_tables();
62
63 set_transient('presslearn_plugin_activation_redirect', true, 30);
64 }
65
66 function presslearn_create_tables() {
67 global $wpdb;
68
69 $charset_collate = $wpdb->get_charset_collate();
70
71 $table_name = $wpdb->prefix . 'presslearn_logs';
72
73 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
74 id bigint(20) NOT NULL AUTO_INCREMENT,
75 time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
76 event varchar(255) NOT NULL,
77 details longtext NOT NULL,
78 PRIMARY KEY (id)
79 ) $charset_collate;";
80
81 $banners_table = $wpdb->prefix . 'presslearn_banners';
82 $sql .= "CREATE TABLE IF NOT EXISTS $banners_table (
83 id bigint(20) NOT NULL AUTO_INCREMENT,
84 name varchar(255) NOT NULL,
85 type varchar(50) NOT NULL DEFAULT 'custom',
86 banner_url text,
87 cover_banner_url text,
88 link text,
89 iframe_code longtext,
90 width int(11),
91 height int(11),
92 status tinyint(1) DEFAULT 1,
93 created_at datetime DEFAULT CURRENT_TIMESTAMP,
94 updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
95 PRIMARY KEY (id)
96 ) $charset_collate;";
97
98 $indexing_table = $wpdb->prefix . 'presslearn_indexing_logs';
99 $sql .= "CREATE TABLE IF NOT EXISTS $indexing_table (
100 id bigint(20) NOT NULL AUTO_INCREMENT,
101 post_id bigint(20) NOT NULL,
102 post_url text NOT NULL,
103 post_title text NOT NULL,
104 request_type varchar(50) NOT NULL DEFAULT 'auto',
105 response_code int(11) DEFAULT NULL,
106 response_message text DEFAULT NULL,
107 indexed_at datetime DEFAULT CURRENT_TIMESTAMP,
108 PRIMARY KEY (id),
109 KEY post_id (post_id),
110 KEY indexed_at (indexed_at)
111 ) $charset_collate;";
112
113 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
114 dbDelta($sql);
115 }
116
117 function presslearn_ensure_banners_table_exists() {
118 global $wpdb;
119
120 $table_name = $wpdb->prefix . 'presslearn_banners';
121
122 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));
123
124 if ($table_exists != $table_name) {
125 $charset_collate = $wpdb->get_charset_collate();
126
127 $sql = "CREATE TABLE $table_name (
128 id bigint(20) NOT NULL AUTO_INCREMENT,
129 name varchar(255) NOT NULL,
130 type varchar(50) NOT NULL DEFAULT 'custom',
131 banner_url text,
132 cover_banner_url text,
133 link text,
134 iframe_code longtext,
135 width int(11),
136 height int(11),
137 status tinyint(1) DEFAULT 1,
138 created_at datetime DEFAULT CURRENT_TIMESTAMP,
139 updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
140 PRIMARY KEY (id)
141 ) $charset_collate;";
142
143 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
144 dbDelta($sql);
145
146 if (function_exists('error_log')) {
147 error_log('PressLearn: wp_presslearn_banners �
148 �이블이 생성되었습니다.');
149 }
150 }
151 }
152
153 function presslearn_plugin_deactivate() {
154 delete_transient('presslearn_plugin_activation_redirect');
155
156 delete_option('presslearn_quick_button_enabled');
157 delete_option('presslearn_button_transition_enabled');
158 delete_option('presslearn_auto_index_enabled');
159 delete_option('presslearn_header_footer_enabled');
160 }
161
162 function presslearn_plugin_activation_redirect() {
163 if (get_transient('presslearn_plugin_activation_redirect')) {
164 delete_transient('presslearn_plugin_activation_redirect');
165 if (is_admin() && !isset($_GET['activate-multi'])) {
166 wp_redirect(admin_url('admin.php?page=presslearn-settings'));
167 exit;
168 }
169 }
170 }
171 add_action('admin_init', 'presslearn_plugin_activation_redirect');
172
173
174 function presslearn_plugin_uninstall() {
175 delete_option('presslearn_plugin_key');
176 delete_option('presslearn_plugin_activated_time');
177 delete_option('presslearn_activation_logs');
178 delete_option('presslearn_plugin_settings');
179
180 delete_option('presslearn_scroll_depth_enabled');
181 delete_option('presslearn_analytics_enabled');
182 delete_option('presslearn_dynamic_banner_enabled');
183 delete_option('presslearn_click_protection_enabled');
184 delete_option('presslearn_ad_clicker_enabled');
185 delete_option('presslearn_social_share_enabled');
186 delete_option('presslearn_quick_button_enabled');
187 delete_option('presslearn_button_transition_enabled');
188 delete_option('presslearn_auto_index_enabled');
189 delete_option('presslearn_header_footer_enabled');
190 delete_transient('presslearn_plugin_activation_redirect');
191
192 $users = get_users(array('fields' => 'ID'));
193 foreach($users as $user_id) {
194 delete_user_meta($user_id, 'presslearn_user_data');
195 }
196
197 presslearn_drop_tables();
198 }
199
200
201 function presslearn_drop_tables() {
202 global $wpdb;
203
204 $table_name = $wpdb->prefix . 'presslearn_logs';
205 $wpdb->query("DROP TABLE IF EXISTS $table_name");
206
207 $banners_table = $wpdb->prefix . 'presslearn_banners';
208 $wpdb->query("DROP TABLE IF EXISTS $banners_table");
209
210 $indexing_table = $wpdb->prefix . 'presslearn_indexing_logs';
211 $wpdb->query("DROP TABLE IF EXISTS $indexing_table");
212 }
213
214 require_once PRESSLEARN_PLUGIN_DIR . 'includes/admin.php';
215 require_once PRESSLEARN_PLUGIN_DIR . 'includes/api.php';
216 require_once PRESSLEARN_PLUGIN_DIR . 'includes/share.php';
217 require_once PRESSLEARN_PLUGIN_DIR . 'includes/buttons.php';
218 require_once PRESSLEARN_PLUGIN_DIR . 'includes/analytics.php';
219 require_once PRESSLEARN_PLUGIN_DIR . 'includes/protection.php';
220 require_once PRESSLEARN_PLUGIN_DIR . 'includes/dynamic.php';
221 require_once PRESSLEARN_PLUGIN_DIR . 'includes/scroll.php';
222 require_once PRESSLEARN_PLUGIN_DIR . 'includes/indexing.php';
223
224 function presslearn_scroll_depth_frontend_script() {
225 if (is_admin()) {
226 return;
227 }
228
229 $scroll_depth_enabled = get_option('presslearn_scroll_depth_enabled', 'no');
230 $popup_content = get_option('presslearn_popup_content', '');
231
232 if ($scroll_depth_enabled !== 'yes' || empty($popup_content)) {
233 return;
234 }
235
236 $scroll_percentage = get_option('presslearn_scroll_percentage', 50);
237 $popup_animation = get_option('presslearn_popup_animation', 'fade');
238 $repeat_setting = get_option('presslearn_repeat_setting', 'once');
239
240 $animation_class = 'popupFadeIn';
241 if ($popup_animation === 'slide') {
242 $animation_class = 'popupSlideIn';
243 } elseif ($popup_animation === 'zoom') {
244 $animation_class = 'popupZoomIn';
245 }
246
247 $cookie_check = $repeat_setting === 'once' ? 'true' : 'false';
248
249 wp_register_style('presslearn-popup-css', false);
250 wp_enqueue_style('presslearn-popup-css');
251
252 $popup_styles = "
253 .pl-popup-overlay {
254 position: fixed;
255 top: 0;
256 left: 0;
257 width: 100%;
258 height: 100%;
259 background-color: rgba(0, 0, 0, 0.7);
260 z-index: 99999;
261 display: none;
262 justify-content: center;
263 align-items: center;
264 }
265
266 .pl-popup-container {
267 display: flex;
268 justify-content: center;
269 align-items: center;
270 width: 100%;
271 height: 100%;
272 }
273
274 .pl-popup-window {
275 background-color: #fff;
276 width: 90%;
277 max-width: 500px;
278 border-radius: 8px;
279 box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
280 display: flex;
281 flex-direction: column;
282 max-height: 80vh;
283 }
284
285 .pl-popup-body {
286 padding: 30px;
287 overflow-y: auto;
288 flex: 1;
289 line-height: 1.6;
290 }
291
292 .pl-popup-body img {
293 max-width: 100%;
294 height: auto;
295 display: block;
296 margin: 0 auto;
297 }
298
299 .pl-close-popup {
300 position: fixed;
301 top: 20px;
302 right: 20px;
303 background: none;
304 border: none;
305 font-size: 40px;
306 cursor: pointer;
307 color: #fff;
308 z-index: 100000;
309 padding: 0;
310 line-height: 1;
311 }
312
313 .pl-close-popup:hover {
314 color: #ddd;
315 }
316
317 @keyframes popupFadeIn {
318 from {
319 opacity: 0;
320 transform: scale(0.9);
321 }
322 to {
323 opacity: 1;
324 transform: scale(1);
325 }
326 }
327
328 @keyframes popupSlideIn {
329 from {
330 opacity: 0;
331 transform: translateY(-50px);
332 }
333 to {
334 opacity: 1;
335 transform: translateY(0);
336 }
337 }
338
339 @keyframes popupZoomIn {
340 from {
341 opacity: 0;
342 transform: scale(0.5);
343 }
344 to {
345 opacity: 1;
346 transform: scale(1);
347 }
348 }
349
350 .popup-animation {
351 animation: " . $animation_class . " 0.3s ease-out;
352 }
353
354 @media screen and (max-width: 768px) {
355 .pl-popup-window {
356 width: 95%;
357 max-width: 95%;
358 }
359
360 .pl-popup-body {
361 padding: 20px;
362 }
363
364 .pl-close-popup {
365 top: 10px;
366 right: 10px;
367 font-size: 32px;
368 }
369 }";
370
371 wp_add_inline_style('presslearn-popup-css', $popup_styles);
372
373 add_action('wp_footer', function() use ($popup_content) {
374 ?>
375 <div id="pl-popup-overlay" class="pl-popup-overlay">
376 <button type="button" class="pl-close-popup">&times;</button>
377 <div class="pl-popup-container">
378 <div class="pl-popup-window popup-animation">
379 <div class="pl-popup-body">
380 <?php echo wp_kses_post(wpautop($popup_content)); ?>
381 </div>
382 </div>
383 </div>
384 </div>
385 <?php
386 });
387
388 wp_register_script('presslearn-popup-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
389 wp_enqueue_script('presslearn-popup-js');
390
391 $popup_script = "
392 (function() {
393 function setCookie(name, value, days) {
394 var expires = '';
395 if (days) {
396 var date = new Date();
397 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
398 expires = '; expires=' + date.toUTCString();
399 }
400 document.cookie = name + '=' + (value || '') + expires + '; path=/';
401 }
402
403 function getCookie(name) {
404 var nameEQ = name + '=';
405 var ca = document.cookie.split(';');
406 for(var i=0; i < ca.length; i++) {
407 var c = ca[i];
408 while (c.charAt(0) == ' ') c = c.substring(1, c.length);
409 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
410 }
411 return null;
412 }
413
414 var cookieName = 'pl_popup_shown';
415 var checkCookie = " . esc_js($cookie_check) . ";
416
417 if (checkCookie && getCookie(cookieName)) {
418 return;
419 }
420
421 var scrollTriggered = false;
422 window.addEventListener('scroll', function() {
423 if (scrollTriggered) return;
424
425 var scrollPercentage = " . esc_js($scroll_percentage) . ";
426 var scrollPosition = window.scrollY;
427 var documentHeight = document.documentElement.scrollHeight - window.innerHeight;
428 var currentScrollPercent = (scrollPosition / documentHeight) * 100;
429
430 if (currentScrollPercent >= scrollPercentage) {
431 scrollTriggered = true;
432 document.getElementById('pl-popup-overlay').style.display = 'flex';
433
434 if (checkCookie) {
435 setCookie(cookieName, 'true', 30);
436 }
437 }
438 });
439
440 document.querySelector('.pl-close-popup').addEventListener('click', function() {
441 document.getElementById('pl-popup-overlay').style.display = 'none';
442 });
443
444 document.getElementById('pl-popup-overlay').addEventListener('click', function(e) {
445 if (e.target === this || e.target.classList.contains('pl-popup-container')) {
446 this.style.display = 'none';
447 }
448 });
449 })();";
450
451 wp_add_inline_script('presslearn-popup-js', $popup_script);
452 }
453
454 add_action('wp_enqueue_scripts', 'presslearn_scroll_depth_frontend_script');
455
456 class PressLearn_Plugin {
457 private static $instance = null;
458
459 private $is_activated = false;
460
461 private $option_key = 'presslearn_plugin_key';
462
463 private $exempt_pages = array('presslearn-settings');
464
465 private $db_version = '1.0';
466
467 public static function get_instance() {
468 if (null === self::$instance) {
469 self::$instance = new self();
470 }
471 return self::$instance;
472 }
473
474
475 private function __construct() {
476 add_action('init', array($this, 'init'));
477
478 add_action('admin_menu', array($this, 'register_admin_menu'));
479
480 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
481
482 $this->check_activation();
483
484 add_action('admin_init', array($this, 'check_page_access'));
485
486 add_action('admin_init', array($this, 'check_version_upgrade'));
487
488 add_action('admin_notices', array($this, 'show_update_notice'));
489
490 add_action('admin_bar_menu', array($this, 'add_admin_bar_menu'), 100);
491 }
492
493
494 public function check_version_upgrade() {
495 if (!function_exists('get_plugin_data')) {
496 require_once(ABSPATH . 'wp-admin/includes/plugin.php');
497 }
498
499 $plugin_data = get_plugin_data(__FILE__);
500 $current_header_version = $plugin_data['Version'];
501
502 $saved_header_version = get_option('presslearn_plugin_header_version', '');
503
504 if (!empty($saved_header_version) && $saved_header_version !== $current_header_version) {
505 update_option('presslearn_plugin_key', '');
506 $this->is_activated = false;
507
508 set_transient('presslearn_plugin_updated_notice', array(
509 'from_version' => $saved_header_version,
510 'to_version' => $current_header_version,
511 'updated_time' => current_time('mysql')
512 ), 86400);
513
514 $this->log_upgrade($saved_header_version, $current_header_version);
515 }
516
517 update_option('presslearn_plugin_header_version', $current_header_version);
518
519 $settings = get_option('presslearn_plugin_settings', array());
520 $current_version = isset($settings['version']) ? $settings['version'] : '0';
521 $current_db_version = isset($settings['db_version']) ? $settings['db_version'] : '0';
522
523 if ($current_version != PRESSLEARN_PLUGIN_VERSION) {
524 if (version_compare($current_db_version, $this->db_version, '<')) {
525 $this->run_db_migration($current_db_version);
526 }
527
528 $settings['version'] = PRESSLEARN_PLUGIN_VERSION;
529 $settings['db_version'] = $this->db_version;
530 $settings['last_upgraded'] = current_time('mysql');
531
532 update_option('presslearn_plugin_settings', $settings);
533 }
534 }
535
536
537 private function run_db_migration($from_version) {
538 global $wpdb;
539
540 if (version_compare($from_version, '0.5', '<')) {
541 $table_name = $wpdb->prefix . 'presslearn_logs';
542
543 $wpdb->query("ALTER TABLE $table_name ADD ip_address varchar(45) DEFAULT '' AFTER event");
544 }
545
546 if (version_compare($from_version, '0.8', '<')) {
547
548 }
549
550 }
551
552 private function log_upgrade($from_version, $to_version) {
553 global $wpdb;
554
555 $table_name = $wpdb->prefix . 'presslearn_logs';
556
557 $wpdb->insert(
558 $table_name,
559 array(
560 'time' => current_time('mysql'),
561 'event' => 'plugin_upgrade',
562 'details' => json_encode(array(
563 'from_version' => $from_version,
564 'to_version' => $to_version,
565 'user_id' => get_current_user_id(),
566 'site_url' => get_site_url()
567 ))
568 )
569 );
570 }
571
572 public function show_update_notice() {
573 $update_notice = get_transient('presslearn_plugin_updated_notice');
574
575 if ($update_notice && is_array($update_notice)) {
576 $from_version = esc_html($update_notice['from_version']);
577 $to_version = esc_html($update_notice['to_version']);
578 $updated_time = esc_html($update_notice['updated_time']);
579
580 ?>
581
582 <script type="text/javascript">
583 jQuery(document).ready(function($) {
584 $('#presslearn-update-notice .notice-dismiss').on('click', function() {
585 $.post(ajaxurl, {
586 action: 'presslearn_dismiss_update_notice',
587 nonce: '<?php echo esc_js(wp_create_nonce('presslearn_dismiss_notice')); ?>'
588 });
589 });
590 });
591 </script>
592 <?php
593 }
594 }
595
596 public function check_page_access() {
597 $current_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
598
599 if (strpos($current_page, 'presslearn-') === 0 && !in_array($current_page, $this->exempt_pages)) {
600 if (!$this->is_activated) {
601 wp_redirect(admin_url('admin.php?page=presslearn-settings&access_denied=true'));
602 exit;
603 }
604 }
605 }
606
607 /**
608 * Initialize
609 */
610 public function init() {
611 if (isset($_GET['presslearn_key']) && !empty($_GET['presslearn_key'])) {
612 if (!current_user_can('manage_options')) {
613 wp_die('관리자 권한이 필요합니다.');
614 }
615
616 if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'presslearn_activate_plugin')) {
617 wp_die('보안 검증에 실패했습니다.');
618 }
619
620 $this->activate_plugin(sanitize_text_field(wp_unslash($_GET['presslearn_key'])));
621 wp_redirect(admin_url('admin.php?page=presslearn-settings&activated=true'));
622 exit;
623 }
624
625 if (get_option('presslearn_analytics_enabled', 'no') === 'yes') {
626 $tables_created = $this->create_analytics_tables();
627
628 if ($tables_created) {
629 add_action('wp_head', array($this, 'add_tracking_code'));
630 $this->register_tracking_ajax();
631 } else {
632 update_option('presslearn_analytics_enabled', 'no');
633
634 if (is_admin()) {
635 add_action('admin_notices', function() {
636 ?>
637 <div class="notice notice-error">
638 <p><?php echo esc_html('씬 애널리틱스 기능 활성화 중 오류가 발생하여 비활성화되었습니다. 데이터베이스 �
639 �이블을 생성할 수 없습니다.'); ?></p>
640 </div>
641 <?php
642 });
643 }
644 }
645 }
646 }
647
648 public function enqueue_admin_scripts($hook) {
649 if (strpos($hook, 'presslearn') === false) {
650 return;
651 }
652
653 wp_enqueue_style('presslearn-admin-css', PRESSLEARN_PLUGIN_URL . 'assets/css/admin.css', array(), PRESSLEARN_PLUGIN_VERSION);
654
655 wp_enqueue_script('jquery');
656 wp_enqueue_script('presslearn-admin-js', PRESSLEARN_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
657
658 wp_register_script('presslearn-chart-js', PRESSLEARN_PLUGIN_URL . 'assets/js/chart.min.js', array(), '3.7.1', true);
659
660 if (strpos($hook, 'presslearn-analytics') !== false) {
661 wp_enqueue_script('presslearn-chart-js');
662 wp_register_style('presslearn-analytics-css', false);
663 wp_enqueue_style('presslearn-analytics-css');
664
665 wp_register_script('presslearn-analytics-js', false, array('jquery', 'presslearn-chart-js', 'moment'), PRESSLEARN_PLUGIN_VERSION, true);
666 wp_enqueue_script('presslearn-analytics-js');
667 }
668
669 if (strpos($hook, 'presslearn-scroll-depth') !== false) {
670 wp_register_style('presslearn-scroll-depth-css', false);
671 wp_enqueue_style('presslearn-scroll-depth-css');
672
673 wp_register_script('presslearn-scroll-depth-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
674 wp_enqueue_script('presslearn-scroll-depth-js');
675 }
676
677 if (strpos($hook, 'presslearn-click-protection') !== false) {
678 wp_register_style('presslearn-click-protection-css', false);
679 wp_enqueue_style('presslearn-click-protection-css');
680
681 wp_register_script('presslearn-click-protection-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
682 wp_enqueue_script('presslearn-click-protection-js');
683 }
684
685 if (strpos($hook, 'presslearn-ad-clicker') !== false) {
686 wp_register_style('presslearn-ad-clicker-css', false);
687 wp_enqueue_style('presslearn-ad-clicker-css');
688
689 wp_register_script('presslearn-ad-clicker-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
690 wp_enqueue_script('presslearn-ad-clicker-js');
691
692 wp_register_script('presslearn-adclicker-admin', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
693 wp_enqueue_script('presslearn-adclicker-admin');
694
695 $adclicker_overlay_range = get_option('presslearn_adclicker_overlay_range', 100);
696 $adclicker_overlay_color = get_option('presslearn_adclicker_overlay_color', '#000000');
697 $adclicker_button_color = get_option('presslearn_adclicker_button_color', '#2196F3');
698 $adclicker_button_text_color = get_option('presslearn_adclicker_button_text_color', '#ffffff');
699 $adclicker_display_time = get_option('presslearn_adclicker_display_time', 'null');
700
701 wp_localize_script('presslearn-adclicker-admin', 'pressleanAdclickerConfig', array(
702 'overlayRange' => $adclicker_overlay_range,
703 'overlayColor' => $adclicker_overlay_color,
704 'buttonColor' => $adclicker_button_color,
705 'buttonTextColor' => $adclicker_button_text_color,
706 'displayTime' => $adclicker_display_time
707 ));
708
709 $adclicker_script = "
710 document.addEventListener('DOMContentLoaded', function() {
711 const previewButton = document.getElementById('preview-adclicker');
712
713 if (previewButton) {
714 previewButton.addEventListener('click', function() {
715 showAdClickerPreview();
716 });
717 }
718
719 const urlParams = new URLSearchParams(window.location.search);
720 const showPreview = urlParams.get('preview');
721
722 if (previewButton && (showPreview === '1' || showPreview === 'true')) {
723 setTimeout(function() {
724 showAdClickerPreview();
725 }, 500);
726 }
727
728 function showAdClickerPreview() {
729 const overlay = document.createElement('div');
730 overlay.id = 'adclicker-overlay-preview';
731 overlay.style.position = 'fixed';
732 overlay.style.bottom = '0';
733 overlay.style.left = '0';
734 overlay.style.width = '100%';
735 overlay.style.height = pressleanAdclickerConfig.overlayRange + 'vh';
736 overlay.style.background = 'linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 0%, ' + pressleanAdclickerConfig.overlayColor + ' 100%)';
737 overlay.style.zIndex = '999999';
738
739 const closeButton = document.createElement('div');
740 closeButton.id = 'adclicker-close-button-preview';
741 closeButton.setAttribute('data-ad-link', '#previewAdLink');
742 closeButton.style.position = 'fixed';
743 closeButton.style.bottom = '60px';
744 closeButton.style.left = '50%';
745 closeButton.style.transform = 'translateX(-50%)';
746 closeButton.style.padding = '15px 30px';
747 closeButton.style.backgroundColor = pressleanAdclickerConfig.buttonColor;
748 closeButton.style.color = pressleanAdclickerConfig.buttonTextColor;
749 closeButton.style.border = 'none';
750 closeButton.style.borderRadius = '8px';
751 closeButton.style.fontSize = '20px';
752 closeButton.style.cursor = 'pointer';
753 closeButton.style.zIndex = '1000001';
754 closeButton.style.textDecoration = 'none';
755 closeButton.style.display = 'inline-block';
756 closeButton.style.textAlign = 'center';
757
758 const previewLink = document.createElement('a');
759 previewLink.href = '#previewAdLink';
760 previewLink.textContent = '미리보기 예시용';
761 previewLink.setAttribute('target', '_blank');
762 previewLink.style.color = 'inherit';
763 previewLink.style.textDecoration = 'inherit';
764 previewLink.style.display = 'block';
765
766 closeButton.appendChild(previewLink);
767
768 const hoverStyle = document.createElement('style');
769 hoverStyle.innerHTML =
770 '#adclicker-close-button-preview:hover,' +
771 '#adclicker-close-button-preview:focus,' +
772 '#adclicker-close-button-preview:active {' +
773 'background-color: ' + closeButton.style.backgroundColor + ' !important;' +
774 'color: ' + closeButton.style.color + ' !important;' +
775 'text-decoration: none !important;' +
776 'outline: none !important;' +
777 '}';
778 document.head.appendChild(hoverStyle);
779
780 const backButtonMessage = document.createElement('div');
781 backButtonMessage.textContent = '원치않으시면 뒤로가기를 해주세요';
782 backButtonMessage.style.position = 'fixed';
783 backButtonMessage.style.bottom = '50px';
784 backButtonMessage.style.left = '50%';
785 backButtonMessage.style.transform = 'translateX(-50%)';
786 backButtonMessage.style.fontSize = '12px';
787 backButtonMessage.style.color = pressleanAdclickerConfig.buttonTextColor;
788 backButtonMessage.style.opacity = '0.8';
789 backButtonMessage.style.zIndex = '1000001';
790 backButtonMessage.style.marginBottom = '-15px';
791
792 const countdownLabel = document.createElement('span');
793 countdownLabel.id = 'adclicker-countdown-label-preview';
794 countdownLabel.style.position = 'absolute';
795 countdownLabel.style.top = '-10px';
796 countdownLabel.style.right = '-10px';
797 countdownLabel.style.width = '30px';
798 countdownLabel.style.height = '30px';
799 countdownLabel.style.borderRadius = '50%';
800 countdownLabel.style.backgroundColor = '#ff3b30';
801 countdownLabel.style.color = 'white';
802 countdownLabel.style.fontSize = '14px';
803 countdownLabel.style.fontWeight = 'bold';
804 countdownLabel.style.textAlign = 'center';
805 countdownLabel.style.lineHeight = '30px';
806 countdownLabel.style.zIndex = '1000002';
807
808 closeButton.addEventListener('click', function(e) {
809 if (e.target.tagName.toLowerCase() !== 'a') {
810 e.preventDefault();
811
812 document.body.removeChild(overlay);
813 document.body.removeChild(closeButton);
814 document.body.removeChild(backButtonMessage);
815 if (countdownInterval) {
816 clearInterval(countdownInterval);
817 }
818 }
819 });
820
821 document.body.appendChild(overlay);
822 document.body.appendChild(closeButton);
823 document.body.appendChild(backButtonMessage);
824
825 const displayTime = pressleanAdclickerConfig.displayTime;
826 let countdownInterval;
827
828 if (displayTime !== 'null') {
829 let timeLeft = parseInt(displayTime);
830 countdownLabel.textContent = timeLeft;
831 closeButton.appendChild(countdownLabel);
832
833 countdownInterval = setInterval(function() {
834 timeLeft--;
835 countdownLabel.textContent = timeLeft;
836
837 if (timeLeft <= 0) {
838 clearInterval(countdownInterval);
839 countdownLabel.textContent = '✕';
840 }
841 }, 1000);
842 }
843 }
844 });
845 ";
846
847 wp_add_inline_script('presslearn-adclicker-admin', $adclicker_script);
848 }
849
850 if (strpos($hook, 'presslearn-dynamic-banner') !== false) {
851 wp_register_style('presslearn-dynamic-banner-css', false);
852 wp_enqueue_style('presslearn-dynamic-banner-css');
853
854 wp_register_script('presslearn-dynamic-banner-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
855 wp_enqueue_script('presslearn-dynamic-banner-js');
856 }
857
858 if (strpos($hook, 'presslearn-social-share') !== false) {
859 wp_register_style('presslearn-social-share-css', false);
860 wp_enqueue_style('presslearn-social-share-css');
861
862 wp_register_script('presslearn-social-share-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
863 wp_enqueue_script('presslearn-social-share-js');
864 }
865
866 if (strpos($hook, 'presslearn-quick-button') !== false) {
867 wp_register_style('presslearn-quick-button-css', false);
868 wp_enqueue_style('presslearn-quick-button-css');
869
870 wp_register_script('presslearn-quick-button-js', false, array('jquery'), PRESSLEARN_PLUGIN_VERSION, true);
871 wp_enqueue_script('presslearn-quick-button-js');
872 }
873
874 wp_localize_script('presslearn-admin-js', 'presslearn_admin', array(
875 'ajax_url' => admin_url('admin-ajax.php'),
876 'nonce' => wp_create_nonce('presslearn_admin_nonce'),
877 'api_url' => rest_url('presslearn/v1'),
878 'site_url' => site_url(),
879 'ip_address' => presslearn_plugin()->get_ip_address(),
880 'is_default_permalink' => empty(get_option('permalink_structure')),
881 'block_expiry_days' => get_option('presslearn_click_protection_block_expiry_days', 30)
882 ));
883 }
884
885 public function register_admin_menu() {
886 $icon_url = PRESSLEARN_PLUGIN_URL . 'assets/images/admin_badge.png';
887
888 add_menu_page(
889 'AL Pack 설정',
890 'AL Pack',
891 'manage_options',
892 'presslearn-settings',
893 array($this, 'render_settings_page'),
894 $icon_url,
895 2
896 );
897
898 add_submenu_page(
899 'presslearn-settings',
900 'AL Pack 대시보드',
901 '대시보드',
902 'manage_options',
903 'presslearn-settings',
904 array($this, 'render_settings_page')
905 );
906
907 if ($this->is_activated) {
908 add_submenu_page(
909 'presslearn-settings',
910 '스마트 스크롤',
911 '스마트 스크롤',
912 'manage_options',
913 'presslearn-scroll-depth',
914 array($this, 'render_advanced_page')
915 );
916
917 add_submenu_page(
918 'presslearn-settings',
919 '씬 애널리틱스',
920 '씬 애널리틱스',
921 'manage_options',
922 'presslearn-analytics',
923 array($this, 'render_analytics_page')
924 );
925
926 add_submenu_page(
927 'presslearn-settings',
928 '애드 프로�
929 �터',
930 '애드 프로�
931 �터',
932 'manage_options',
933 'presslearn-click-protection',
934 array($this, 'render_click_protection_page')
935 );
936
937 add_submenu_page(
938 'presslearn-settings',
939 '애드클리커',
940 '애드클리커',
941 'manage_options',
942 'presslearn-ad-clicker',
943 array($this, 'render_ad_clicker_page')
944 );
945
946 add_submenu_page(
947 'presslearn-settings',
948 '다이나믹 배너',
949 '다이나믹 배너',
950 'manage_options',
951 'presslearn-dynamic-banner',
952 array($this, 'render_dynamic_banner_page')
953 );
954
955 add_submenu_page(
956 'presslearn-settings',
957 '소�
958 � 공유',
959 '소�
960 � 공유',
961 'manage_options',
962 'presslearn-social-share',
963 array($this, 'render_social_share_page')
964 );
965
966 add_submenu_page(
967 'presslearn-settings',
968 '빠른 버튼 생성',
969 '빠른 버튼 생성',
970 'manage_options',
971 'presslearn-quick-button',
972 array($this, 'render_quick_button_page')
973 );
974
975 add_submenu_page(
976 'presslearn-settings',
977 'Ads 매니저',
978 'Ads 매니저',
979 'manage_options',
980 'presslearn-ads',
981 array($this, 'render_ads_page')
982 );
983
984 add_submenu_page(
985 'presslearn-settings',
986 '자동 인덱싱',
987 '자동 인덱싱',
988 'manage_options',
989 'presslearn-auto-index',
990 array($this, 'render_auto_index_page')
991 );
992
993 add_submenu_page(
994 'presslearn-settings',
995 '헤더 & 푸터',
996 '헤더 & 푸터',
997 'manage_options',
998 'presslearn-header-footer',
999 array($this, 'render_header_footer_page')
1000 );
1001 }
1002 }
1003
1004 public function render_settings_page() {
1005 $show_activated_notice = false;
1006 $show_access_denied_notice = false;
1007
1008 if (current_user_can('manage_options') && is_admin()) {
1009 if (isset($_GET['activated']) && sanitize_text_field(wp_unslash($_GET['activated'])) === 'true') {
1010 $show_activated_notice = true;
1011 }
1012
1013 if (isset($_GET['access_denied']) && sanitize_text_field(wp_unslash($_GET['access_denied'])) === 'true') {
1014 $show_access_denied_notice = true;
1015 }
1016 }
1017
1018 if ($show_activated_notice) {
1019 ?>
1020 <div class="notice notice-success is-dismissible">
1021 <p>프레스런 통합 플러그인이 성공적으로 활성화되었습니다</p>
1022 </div>
1023 <?php
1024 }
1025
1026 if ($show_access_denied_notice) {
1027 ?>
1028 <div class="notice notice-error is-dismissible">
1029 <p>프레스런 통합 플러그인이 활성화되지 않았습니다. 플러그인을 활성화해야 모든 기능에 접근할 있습니다.</p>
1030 </div>
1031 <?php
1032 }
1033
1034 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-settings.php';
1035 }
1036
1037
1038 public function render_analytics_page() {
1039 if (!$this->verify_activation()) {
1040 return;
1041 }
1042
1043 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-analytics.php';
1044 }
1045
1046 public function render_advanced_page() {
1047 if (!$this->verify_activation()) {
1048 return;
1049 }
1050
1051 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-scroll-depth.php';
1052 }
1053
1054 public function render_click_protection_page() {
1055 if (!$this->verify_activation()) {
1056 return;
1057 }
1058
1059 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-click-protection.php';
1060 }
1061
1062 public function render_ad_clicker_page() {
1063 if (!$this->verify_activation()) {
1064 return;
1065 }
1066
1067 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-adclicker.php';
1068 }
1069
1070 public function render_dynamic_banner_page() {
1071 if (!$this->verify_activation()) {
1072 return;
1073 }
1074
1075 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-dynamic-banner.php';
1076 }
1077
1078 public function render_social_share_page() {
1079 if (!$this->verify_activation()) {
1080 return;
1081 }
1082
1083 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-social-share.php';
1084 }
1085
1086 public function render_quick_button_page() {
1087 if (!$this->verify_activation()) {
1088 return;
1089 }
1090
1091 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-quick-button.php';
1092 }
1093
1094 public function render_ads_page() {
1095 if (!$this->verify_activation()) {
1096 return;
1097 }
1098
1099 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-ads.php';
1100 }
1101
1102 public function render_auto_index_page() {
1103 if (!$this->verify_activation()) {
1104 return;
1105 }
1106
1107 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-auto-index.php';
1108 }
1109
1110 public function render_header_footer_page() {
1111 if (!$this->verify_activation()) {
1112 return;
1113 }
1114
1115 include_once PRESSLEARN_PLUGIN_DIR . 'templates/admin-header-footer.php';
1116 }
1117
1118 private function verify_activation() {
1119 if (!$this->is_activated) {
1120 wp_redirect(admin_url('admin.php?page=presslearn-settings&access_denied=true'));
1121 exit;
1122 }
1123
1124 return true;
1125 }
1126
1127
1128 public function activate_plugin($key) {
1129 $is_valid = strlen($key) >= 32;
1130
1131 if ($is_valid) {
1132 update_option($this->option_key, $key);
1133 $this->is_activated = true;
1134 return true;
1135 }
1136
1137 return false;
1138 }
1139
1140 private function check_activation() {
1141 $key = get_option($this->option_key, '');
1142 $this->is_activated = !empty($key);
1143 }
1144
1145 public function is_plugin_activated() {
1146 return $this->is_activated;
1147 }
1148
1149 public function get_kakao_login_url() {
1150 $https = isset($_SERVER['HTTPS']) && sanitize_text_field(wp_unslash($_SERVER['HTTPS'])) === 'on';
1151 $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
1152 $request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '';
1153
1154 $current_url = ($https ? "https" : "http") . "://" . $host . $request_uri;
1155 $kakao_login_url = 'https://presslearn.co.kr/login?redirect_url=' . urlencode($current_url) . '&popup=true';
1156 return $kakao_login_url;
1157 }
1158
1159 public function add_tracking_code() {
1160 $analytics_enabled = get_option('presslearn_analytics_enabled', 'no');
1161 $exclude_admin = get_option('presslearn_analytics_exclude_admin', '');
1162
1163 if ($analytics_enabled !== 'yes') {
1164 return;
1165 }
1166
1167 if (current_user_can('manage_options') && get_option('presslearn_analytics_exclude_admin') === 'yes') {
1168 return;
1169 }
1170
1171 $excluded_ips = array_map('trim', explode(',', $exclude_admin));
1172 $user_ip = $this->get_ip_address();
1173
1174 if (in_array($user_ip, $excluded_ips)) {
1175 return;
1176 }
1177
1178 wp_enqueue_script(
1179 'presslearn-analytics-tracking',
1180 PRESSLEARN_PLUGIN_URL . 'assets/js/analytics-tracking.js',
1181 array('jquery'),
1182 PRESSLEARN_PLUGIN_VERSION,
1183 true
1184 );
1185
1186 wp_localize_script(
1187 'presslearn-analytics-tracking',
1188 'pressleanAnalytics',
1189 array(
1190 'ajaxurl' => admin_url('admin-ajax.php'),
1191 'nonce' => wp_create_nonce('presslearn_tracking_nonce')
1192 )
1193 );
1194 }
1195
1196 public function get_ip_address() {
1197 $ip = '';
1198 $use_cloudflare = get_option('presslearn_analytics_use_cloudflare', 'no');
1199
1200 if ($use_cloudflare === 'yes' && isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
1201 $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
1202 } else {
1203 if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
1204 $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CLIENT_IP']));
1205 } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1206 $ip_list = explode(',', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])));
1207 $ip = trim($ip_list[0]);
1208 } else {
1209 $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
1210 }
1211 }
1212
1213 return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '';
1214 }
1215
1216 public function get_visitor_country($ip) {
1217 $ip = filter_var($ip, FILTER_VALIDATE_IP);
1218
1219 if (empty($ip) || $ip == '127.0.0.1' || $ip == '::1') {
1220 return '';
1221 }
1222
1223 $api_url = 'http://ip-api.com/json/' . esc_attr($ip);
1224 $response = wp_remote_get($api_url, array(
1225 'timeout' => 5,
1226 'headers' => array(
1227 'Accept' => 'application/json'
1228 )
1229 ));
1230
1231 if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
1232 $data = json_decode(wp_remote_retrieve_body($response), true);
1233 if (isset($data['country']) && $data['status'] === 'success') {
1234 return sanitize_text_field($data['country']);
1235 }
1236 }
1237
1238 return '';
1239 }
1240
1241 public function register_tracking_ajax() {
1242 add_action('wp_ajax_presslearn_track_pageview', array($this, 'track_pageview'));
1243 add_action('wp_ajax_nopriv_presslearn_track_pageview', array($this, 'track_pageview'));
1244 }
1245
1246 public function track_pageview() {
1247 check_ajax_referer('presslearn_tracking_nonce', 'nonce');
1248
1249 $visitor_id = isset($_POST['visitor_id']) ? sanitize_text_field(wp_unslash($_POST['visitor_id'])) : '';
1250 $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';
1251 $title = isset($_POST['title']) ? sanitize_text_field(wp_unslash($_POST['title'])) : '';
1252 $referrer = isset($_POST['referrer']) ? esc_url_raw(wp_unslash($_POST['referrer'])) : '';
1253 $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
1254 $ip = $this->get_ip_address();
1255 $country = $this->get_visitor_country($ip);
1256
1257 if (!empty($referrer)) {
1258 if (strpos($referrer, 'http') !== 0 && strpos($referrer, '//') !== 0) {
1259 $referrer = 'https://' . $referrer;
1260 }
1261
1262 if (strpos($referrer, '//') === 0) {
1263 $referrer = 'https:' . $referrer;
1264 }
1265
1266 $site_host = wp_parse_url(site_url(), PHP_URL_HOST);
1267 $referrer_host = wp_parse_url($referrer, PHP_URL_HOST);
1268
1269 if ($referrer_host === $site_host) {
1270 }
1271
1272 } else {
1273 }
1274
1275 $this->create_analytics_tables();
1276
1277 global $wpdb;
1278 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
1279 $table_visitors = $wpdb->prefix . 'presslearn_visitors';
1280
1281 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
1282 $this->create_analytics_tables();
1283 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
1284 wp_send_json_error('데이터베이스 �
1285 �이블을 생성할 수 없습니다.');
1286 wp_die();
1287 }
1288 }
1289
1290 $result = $wpdb->insert(
1291 $table_pageviews,
1292 array(
1293 'url' => $url,
1294 'title' => $title,
1295 'visitor_id' => $visitor_id,
1296 'referrer' => $referrer,
1297 'user_agent' => $user_agent,
1298 'country' => $country,
1299 'ip' => $ip,
1300 'created_at' => current_time('mysql')
1301 )
1302 );
1303
1304 if ($wpdb->last_error) {
1305 wp_send_json_error('데이터베이스 오류: ' . $wpdb->last_error);
1306 wp_die();
1307 } else {
1308 error_log('Pageview record inserted successfully: ' . $result);
1309
1310 $post_id = url_to_postid($url);
1311 if ($post_id > 0) {
1312 $cache_date = current_time('Y-m-d');
1313 wp_cache_delete("presslearn_visitors_{$post_id}_{$cache_date}", 'presslearn_analytics');
1314 wp_cache_delete("presslearn_today_{$post_id}_{$cache_date}", 'presslearn_analytics');
1315 wp_cache_delete("presslearn_week_{$post_id}_{$cache_date}", 'presslearn_analytics');
1316 wp_cache_delete("presslearn_month_{$post_id}_{$cache_date}", 'presslearn_analytics');
1317
1318 delete_post_meta($post_id, '_presslearn_post_views');
1319 }
1320 }
1321
1322 $visitor = $wpdb->get_row($wpdb->prepare(
1323 "SELECT * FROM $table_visitors WHERE visitor_id = %s",
1324 $visitor_id
1325 ));
1326
1327 if ($visitor) {
1328 $wpdb->update(
1329 $table_visitors,
1330 array(
1331 'last_visit' => current_time('mysql'),
1332 'visits' => $visitor->visits + 1
1333 ),
1334 array('visitor_id' => $visitor_id)
1335 );
1336 } else {
1337 $wpdb->insert(
1338 $table_visitors,
1339 array(
1340 'visitor_id' => $visitor_id,
1341 'first_visit' => current_time('mysql'),
1342 'last_visit' => current_time('mysql')
1343 )
1344 );
1345 }
1346
1347 if (!empty($referrer)) {
1348 $referrer_host = wp_parse_url($referrer, PHP_URL_HOST);
1349 error_log('Referrer host: ' . $referrer_host);
1350
1351 if (!empty($referrer_host)) {
1352 $table_referrers = $wpdb->prefix . 'presslearn_referrers';
1353
1354 if ($wpdb->get_var("SHOW TABLES LIKE '$table_referrers'") != $table_referrers) {
1355 $this->create_analytics_tables();
1356 }
1357
1358 $existing_referrer = $wpdb->get_row($wpdb->prepare(
1359 "SELECT * FROM $table_referrers WHERE referrer_host = %s",
1360 $referrer_host
1361 ));
1362
1363 if ($existing_referrer) {
1364 $wpdb->update(
1365 $table_referrers,
1366 array(
1367 'count' => $existing_referrer->count + 1,
1368 'last_visit' => current_time('mysql')
1369 ),
1370 array('id' => $existing_referrer->id)
1371 );
1372 } else {
1373 $wpdb->insert(
1374 $table_referrers,
1375 array(
1376 'referrer_host' => $referrer_host,
1377 'referrer_url' => $referrer,
1378 'count' => 1,
1379 'last_visit' => current_time('mysql')
1380 )
1381 );
1382 }
1383
1384 if ($wpdb->last_error) {
1385 error_log('Referrer insert/update error: ' . $wpdb->last_error);
1386 } else {
1387 error_log('Referrer record processed for: ' . $referrer_host);
1388 }
1389 }
1390 }
1391
1392 wp_send_json_success();
1393 wp_die();
1394 }
1395
1396 public function create_analytics_tables() {
1397 global $wpdb;
1398 $charset_collate = $wpdb->get_charset_collate();
1399
1400 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
1401 $table_visitors = $wpdb->prefix . 'presslearn_visitors';
1402 $table_referrers = $wpdb->prefix . 'presslearn_referrers';
1403
1404 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1405
1406 $wpdb->suppress_errors();
1407
1408 $tables_created = true;
1409
1410 if($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
1411 $sql = "CREATE TABLE IF NOT EXISTS $table_pageviews (
1412 id bigint(20) NOT NULL AUTO_INCREMENT,
1413 url varchar(255) NOT NULL,
1414 title text NOT NULL,
1415 visitor_id varchar(32) NOT NULL,
1416 referrer text,
1417 user_agent text,
1418 country varchar(50),
1419 ip varchar(100),
1420 created_at datetime DEFAULT CURRENT_TIMESTAMP,
1421 PRIMARY KEY (id),
1422 KEY visitor_id (visitor_id),
1423 KEY url (url),
1424 KEY created_at (created_at)
1425 ) $charset_collate;";
1426
1427 $result = dbDelta($sql);
1428 if (empty($result)) {
1429 $tables_created = false;
1430 error_log('Error creating presslearn_pageviews table');
1431 }
1432 }
1433
1434 if($wpdb->get_var("SHOW TABLES LIKE '$table_visitors'") != $table_visitors) {
1435 $sql = "CREATE TABLE IF NOT EXISTS $table_visitors (
1436 id bigint(20) NOT NULL AUTO_INCREMENT,
1437 visitor_id varchar(32) NOT NULL,
1438 first_visit datetime DEFAULT CURRENT_TIMESTAMP,
1439 last_visit datetime DEFAULT CURRENT_TIMESTAMP,
1440 visits int(11) DEFAULT 1,
1441 PRIMARY KEY (id),
1442 UNIQUE KEY visitor_id (visitor_id),
1443 KEY first_visit (first_visit),
1444 KEY last_visit (last_visit)
1445 ) $charset_collate;";
1446
1447 $result = dbDelta($sql);
1448 if (empty($result)) {
1449 $tables_created = false;
1450 error_log('Error creating presslearn_visitors table');
1451 }
1452 }
1453
1454 if($wpdb->get_var("SHOW TABLES LIKE '$table_referrers'") != $table_referrers) {
1455 $sql = "CREATE TABLE IF NOT EXISTS $table_referrers (
1456 id bigint(20) NOT NULL AUTO_INCREMENT,
1457 referrer_host varchar(255),
1458 referrer_url text,
1459 count int(11) DEFAULT 1,
1460 last_visit datetime DEFAULT CURRENT_TIMESTAMP,
1461 PRIMARY KEY (id),
1462 KEY referrer_host (referrer_host),
1463 KEY last_visit (last_visit)
1464 ) $charset_collate;";
1465
1466 $result = dbDelta($sql);
1467 if (empty($result)) {
1468 $tables_created = false;
1469 error_log('Error creating presslearn_referrers table');
1470 }
1471 }
1472
1473 return $tables_created;
1474 }
1475
1476 public function get_visitor_ip_for_protection() {
1477 $use_cloudflare = get_option('presslearn_click_protection_use_cloudflare', 'no');
1478
1479 $ip = '';
1480
1481 if ($use_cloudflare === 'yes' && isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
1482 $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
1483 }
1484 else {
1485 if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
1486 $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CLIENT_IP']));
1487 } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1488 $ip_list = explode(',', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])));
1489 $ip = trim($ip_list[0]);
1490 } else {
1491 $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
1492 }
1493 }
1494
1495 return $ip;
1496 }
1497
1498 public function add_admin_bar_menu($wp_admin_bar) {
1499 if (!$this->is_activated) {
1500 return;
1501 }
1502
1503 $wp_admin_bar->add_node(array(
1504 'id' => 'presslearn-menu',
1505 'title' => 'AL Pack',
1506 'href' => admin_url('admin.php?page=presslearn-settings'),
1507 'meta' => array(
1508 'title' => 'AL Pack'
1509 )
1510 ));
1511
1512 $wp_admin_bar->add_node(array(
1513 'id' => 'presslearn-settings',
1514 'parent' => 'presslearn-menu',
1515 'title' => '대시보드',
1516 'href' => admin_url('admin.php?page=presslearn-settings')
1517 ));
1518
1519 $wp_admin_bar->add_node(array(
1520 'id' => 'presslearn-analytics',
1521 'parent' => 'presslearn-menu',
1522 'title' => '씬 애널리틱스',
1523 'href' => admin_url('admin.php?page=presslearn-analytics')
1524 ));
1525
1526 $wp_admin_bar->add_node(array(
1527 'id' => 'presslearn-scroll-depth',
1528 'parent' => 'presslearn-menu',
1529 'title' => '스마트 스크롤',
1530 'href' => admin_url('admin.php?page=presslearn-scroll-depth')
1531 ));
1532
1533 $wp_admin_bar->add_node(array(
1534 'id' => 'presslearn-click-protection',
1535 'parent' => 'presslearn-menu',
1536 'title' => '애드 프로�
1537 �터',
1538 'href' => admin_url('admin.php?page=presslearn-click-protection')
1539 ));
1540
1541 $wp_admin_bar->add_node(array(
1542 'id' => 'presslearn-ad-clicker',
1543 'parent' => 'presslearn-menu',
1544 'title' => '애드클리커',
1545 'href' => admin_url('admin.php?page=presslearn-ad-clicker')
1546 ));
1547
1548 $wp_admin_bar->add_node(array(
1549 'id' => 'presslearn-dynamic-banner',
1550 'parent' => 'presslearn-menu',
1551 'title' => '다이나믹 배너',
1552 'href' => admin_url('admin.php?page=presslearn-dynamic-banner')
1553 ));
1554
1555 $wp_admin_bar->add_node(array(
1556 'id' => 'presslearn-social-share',
1557 'parent' => 'presslearn-menu',
1558 'title' => '소�
1559 � 공유',
1560 'href' => admin_url('admin.php?page=presslearn-social-share')
1561 ));
1562
1563 $wp_admin_bar->add_node(array(
1564 'id' => 'presslearn-quick-button',
1565 'parent' => 'presslearn-menu',
1566 'title' => '빠른 버튼 생성',
1567 'href' => admin_url('admin.php?page=presslearn-quick-button')
1568 ));
1569
1570 $wp_admin_bar->add_node(array(
1571 'id' => 'presslearn-ads',
1572 'parent' => 'presslearn-menu',
1573 'title' => 'Ads 매니저',
1574 'href' => admin_url('admin.php?page=presslearn-ads')
1575 ));
1576
1577 $wp_admin_bar->add_node(array(
1578 'id' => 'presslearn-auto-index',
1579 'parent' => 'presslearn-menu',
1580 'title' => '자동 인덱싱',
1581 'href' => admin_url('admin.php?page=presslearn-auto-index')
1582 ));
1583
1584 $wp_admin_bar->add_node(array(
1585 'id' => 'presslearn-header-footer',
1586 'parent' => 'presslearn-menu',
1587 'title' => '헤더 & 푸터',
1588 'href' => admin_url('admin.php?page=presslearn-header-footer')
1589 ));
1590 }
1591 }
1592
1593 function presslearn_plugin() {
1594 return PressLearn_Plugin::get_instance();
1595 }
1596
1597 presslearn_plugin();
1598
1599 add_action('wp_ajax_presslearn_get_allowed_ips', 'presslearn_get_allowed_ips');
1600 add_action('wp_ajax_presslearn_add_allowed_ip', 'presslearn_add_allowed_ip');
1601 add_action('wp_ajax_presslearn_delete_allowed_ip', 'presslearn_delete_allowed_ip');
1602 add_action('wp_ajax_presslearn_get_blocked_ips', 'presslearn_get_blocked_ips');
1603 add_action('wp_ajax_presslearn_add_blocked_ip', 'presslearn_add_blocked_ip');
1604 add_action('wp_ajax_presslearn_delete_blocked_ip', 'presslearn_delete_blocked_ip');
1605 add_action('wp_ajax_presslearn_delete_analytics_data', 'presslearn_delete_analytics_data');
1606
1607 function presslearn_get_allowed_ips() {
1608 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1609
1610 if (current_user_can('manage_options')) {
1611 $allowed_ips = get_option('presslearn_click_protection_allowed_ips', array());
1612
1613 wp_send_json_success(array(
1614 'ips' => $allowed_ips
1615 ));
1616 }
1617
1618 wp_send_json_error(array('message' => '권한이 없습니다.'));
1619 wp_die();
1620 }
1621
1622 function presslearn_add_allowed_ip() {
1623 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1624
1625 if (current_user_can('manage_options')) {
1626 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
1627
1628 if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) {
1629 wp_send_json_error(array('message' => '유효하지 않은 IP 주소�
1630 니다.'));
1631 wp_die();
1632 }
1633
1634 $allowed_ips = get_option('presslearn_click_protection_allowed_ips', array());
1635
1636 foreach ($allowed_ips as $item) {
1637 if ($item['ip'] === $ip) {
1638 wp_send_json_error(array('message' => '이미 등록된 IP 주소�
1639 니다.'));
1640 wp_die();
1641 }
1642 }
1643
1644 $allowed_ips[] = array(
1645 'ip' => $ip,
1646 'date' => current_time('Y-m-d')
1647 );
1648
1649 update_option('presslearn_click_protection_allowed_ips', $allowed_ips);
1650
1651 wp_send_json_success(array(
1652 'message' => 'IP가 성공적으로 추가되었습니다.',
1653 'ips' => $allowed_ips
1654 ));
1655 }
1656
1657 wp_die();
1658 }
1659
1660 function presslearn_delete_allowed_ip() {
1661 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1662
1663 if (current_user_can('manage_options')) {
1664 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
1665
1666 if (empty($ip)) {
1667 wp_send_json_error(array('message' => '삭제할 IP 주소가 지정되지 않았습니다.'));
1668 wp_die();
1669 }
1670
1671 $allowed_ips = get_option('presslearn_click_protection_allowed_ips', array());
1672
1673 foreach ($allowed_ips as $key => $item) {
1674 if ($item['ip'] === $ip) {
1675 unset($allowed_ips[$key]);
1676 break;
1677 }
1678 }
1679
1680 $allowed_ips = array_values($allowed_ips);
1681
1682 update_option('presslearn_click_protection_allowed_ips', $allowed_ips);
1683
1684 wp_send_json_success(array(
1685 'message' => 'IP가 성공적으로 삭제되었습니다.',
1686 'ips' => $allowed_ips
1687 ));
1688 }
1689
1690 wp_die();
1691 }
1692
1693 function presslearn_get_blocked_ips() {
1694 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1695
1696 if (current_user_can('manage_options')) {
1697 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
1698
1699 wp_send_json_success(array(
1700 'ips' => $blocked_ips
1701 ));
1702 }
1703
1704 wp_send_json_error(array('message' => '권한이 없습니다.'));
1705 wp_die();
1706 }
1707
1708 function presslearn_add_blocked_ip() {
1709 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1710
1711 if (current_user_can('manage_options')) {
1712 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
1713 $permanent = isset($_POST['permanent']) ? sanitize_text_field(wp_unslash($_POST['permanent'])) : '0';
1714
1715 if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) {
1716 wp_send_json_error(array('message' => '유효하지 않은 IP 주소�
1717 니다.'));
1718 wp_die();
1719 }
1720
1721 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
1722
1723 foreach ($blocked_ips as $item) {
1724 if ($item['ip'] === $ip) {
1725 wp_send_json_error(array('message' => '이미 차단된 IP 주소�
1726 니다.'));
1727 wp_die();
1728 }
1729 }
1730
1731 $current_date = current_time('Y-m-d');
1732
1733 if ($permanent === '1') {
1734 $expires = '';
1735 $reason = '수동 차단 (영구)';
1736 } else {
1737 $block_expiry_days = get_option('presslearn_click_protection_block_expiry_days', 30);
1738 $expires = $block_expiry_days > 0 ? gmdate('Y-m-d', strtotime("+{$block_expiry_days} days")) : '';
1739 $reason = '수동 차단';
1740 }
1741
1742 $blocked_ips[] = array(
1743 'ip' => $ip,
1744 'date' => $current_date,
1745 'block_date' => $current_date,
1746 'reason' => $reason,
1747 'expires' => $expires
1748 );
1749
1750 update_option('presslearn_click_protection_blocked_ips', $blocked_ips);
1751
1752 wp_send_json_success(array(
1753 'message' => 'IP가 성공적으로 차단되었습니다.',
1754 'ips' => $blocked_ips
1755 ));
1756 }
1757
1758 wp_die();
1759 }
1760
1761 function presslearn_delete_blocked_ip() {
1762 check_ajax_referer('presslearn_ip_nonce', 'nonce');
1763
1764 if (current_user_can('manage_options')) {
1765 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
1766
1767 if (empty($ip)) {
1768 wp_send_json_error(array('message' => '삭제할 IP 주소가 지정되지 않았습니다.'));
1769 wp_die();
1770 }
1771
1772 $lock_key = 'presslearn_delete_blocked_' . md5($ip);
1773 if (get_transient($lock_key)) {
1774 wp_send_json_error(array('message' => '다른 요청이 처리 중�
1775 니다. 잠시 다시 시도해주세요.'));
1776 wp_die();
1777 }
1778
1779 set_transient($lock_key, true, 3);
1780
1781 try {
1782 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
1783 $ip_found = false;
1784
1785 foreach ($blocked_ips as $key => $item) {
1786 if ($item['ip'] === $ip) {
1787 unset($blocked_ips[$key]);
1788 $ip_found = true;
1789 break;
1790 }
1791 }
1792
1793 if (!$ip_found) {
1794 delete_transient($lock_key);
1795 wp_send_json_error(array('message' => '차단 목록에서 해당 IP를 찾을 없습니다.'));
1796 wp_die();
1797 }
1798
1799 $blocked_ips = array_values($blocked_ips);
1800
1801 update_option('presslearn_click_protection_blocked_ips_backup_delete', get_option('presslearn_click_protection_blocked_ips', array()));
1802
1803 update_option('presslearn_click_protection_blocked_ips', $blocked_ips);
1804
1805 $reset_signals = get_option('presslearn_reset_localStorage_signals', array());
1806 $reset_signals[$ip] = array(
1807 'timestamp' => time(),
1808 'expires' => time() + 3600
1809 );
1810 update_option('presslearn_reset_localStorage_signals', $reset_signals);
1811
1812 delete_transient($lock_key);
1813
1814 wp_send_json_success(array(
1815 'message' => 'IP 차단이 성공적으로 해제되었습니다.',
1816 'ips' => $blocked_ips,
1817 'reset_signal_sent' => true
1818 ));
1819
1820 } catch (Exception $e) {
1821 delete_transient($lock_key);
1822 wp_send_json_error(array('message' => '서버 오류가 발생했습니다.'));
1823 }
1824 }
1825
1826 wp_die();
1827 }
1828
1829 add_action('init', 'presslearn_setup_cron_for_ip_unblock');
1830
1831 function presslearn_setup_cron_for_ip_unblock() {
1832 if (!wp_next_scheduled('presslearn_check_blocked_ips_expiry')) {
1833 wp_schedule_event(strtotime('tomorrow midnight'), 'daily', 'presslearn_check_blocked_ips_expiry');
1834 }
1835 }
1836
1837 add_action('presslearn_check_blocked_ips_expiry', 'presslearn_check_and_remove_expired_blocked_ips');
1838
1839 function presslearn_cleanup_expired_reset_signals() {
1840 $reset_signals = get_option('presslearn_reset_localStorage_signals', array());
1841 $current_time = time();
1842 $updated = false;
1843
1844 foreach ($reset_signals as $ip => $signal) {
1845 if (isset($signal['expires']) && $current_time > $signal['expires']) {
1846 unset($reset_signals[$ip]);
1847 $updated = true;
1848 }
1849 }
1850
1851 if ($updated) {
1852 update_option('presslearn_reset_localStorage_signals', $reset_signals);
1853 }
1854 }
1855
1856 add_action('presslearn_check_blocked_ips_expiry', 'presslearn_cleanup_expired_reset_signals');
1857
1858 function presslearn_check_and_remove_expired_blocked_ips() {
1859 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
1860 if (empty($blocked_ips)) {
1861 return;
1862 }
1863
1864 $block_expiry_days = get_option('presslearn_click_protection_block_expiry_days', 30);
1865
1866 $current_date = new DateTime(current_time('Y-m-d'));
1867 $updated = false;
1868
1869 $new_blocked_ips = array();
1870
1871 foreach ($blocked_ips as $item) {
1872 $block_date = isset($item['block_date']) ? $item['block_date'] : $item['date'];
1873
1874 $block_date_obj = new DateTime($block_date);
1875
1876 $interval = $current_date->diff($block_date_obj);
1877 $days_since_blocked = $interval->days;
1878
1879 if ($days_since_blocked < $block_expiry_days) {
1880 $new_blocked_ips[] = $item;
1881 } else {
1882 $updated = true;
1883 }
1884 }
1885
1886 if ($updated) {
1887 update_option('presslearn_click_protection_blocked_ips', $new_blocked_ips);
1888 }
1889 }
1890
1891 register_deactivation_hook(__FILE__, 'presslearn_clear_cron_for_ip_unblock');
1892
1893 function presslearn_clear_cron_for_ip_unblock() {
1894 $timestamp = wp_next_scheduled('presslearn_check_blocked_ips_expiry');
1895 if ($timestamp) {
1896 wp_unschedule_event($timestamp, 'presslearn_check_blocked_ips_expiry');
1897 }
1898
1899 delete_option('presslearn_reset_localStorage_signals');
1900
1901 delete_option('presslearn_click_protection_blocked_ips_backup');
1902 delete_option('presslearn_click_protection_blocked_ips_backup_delete');
1903 }
1904
1905 function presslearn_block_ads_for_blocked_ips() {
1906 if (is_admin()) {
1907 return;
1908 }
1909
1910 $click_protection_enabled = get_option('presslearn_click_protection_enabled', 'no');
1911 if ($click_protection_enabled !== 'yes') {
1912 return;
1913 }
1914
1915 $use_cloudflare = get_option('presslearn_click_protection_use_cloudflare', 'no');
1916
1917 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
1918 if (empty($blocked_ips)) {
1919 return;
1920 }
1921
1922 $user_ip = presslearn_plugin()->get_visitor_ip_for_protection();
1923
1924 $is_blocked = false;
1925
1926 foreach ($blocked_ips as $item) {
1927 if ($item['ip'] === $user_ip) {
1928 $is_blocked = true;
1929 break;
1930 }
1931 }
1932
1933 if (!$is_blocked) {
1934 return;
1935 }
1936
1937 setcookie('pl_ad_blocked', '1', time() + 86400, '/');
1938
1939 wp_register_style('presslearn-ad-block-css', false);
1940 wp_enqueue_style('presslearn-ad-block-css');
1941
1942 $ad_block_css = "
1943 #google_esf,
1944 .adsbygoogle,
1945 iframe[id^=\"google_ads_\"],
1946 ins.adsbygoogle,
1947 ins.adsbygoogle-noablate,
1948 iframe[id^=\"aswift_\"],
1949 div[id^=\"aswift_\"],
1950 [data-ad-status],
1951 [data-adsbygoogle-status],
1952 [data-google-query-id],
1953 [data-google-container-id],
1954 [data-ad-format],
1955 ins[class*=\"adsbygoogle\"] {
1956 display: none !important;
1957 visibility: hidden !important;
1958 opacity: 0 !important;
1959 width: 0 !important;
1960 height: 0 !important;
1961 position: absolute !important;
1962 left: -9999px !important;
1963 top: -9999px !important;
1964 pointer-events: none !important;
1965 max-width: 0 !important;
1966 max-height: 0 !important;
1967 overflow: hidden !important;
1968 }
1969
1970 .right-side-rail-edge,
1971 .right-side-rail-dismiss-btn,
1972 [data-side-rail-status] {
1973 display: none !important;
1974 visibility: hidden !important;
1975 opacity: 0 !important;
1976 }
1977 ";
1978
1979 wp_add_inline_style('presslearn-ad-block-css', $ad_block_css);
1980
1981 wp_register_script('presslearn-ad-block-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
1982 wp_enqueue_script('presslearn-ad-block-js');
1983
1984 $ad_block_script = "
1985 (function() {
1986 function preventAdLoad() {
1987
1988 window.adsbygoogle = [];
1989 window.google_ad_status = 1;
1990
1991 var adScripts = document.querySelectorAll('script[src*=\"pagead2.googlesyndication.com\"], script[src*=\"googleads\"], script[src*=\"adsbygoogle\"]');
1992 adScripts.forEach(function(script) {
1993 if (script && script.parentNode) {
1994 script.parentNode.removeChild(script);
1995 }
1996 });
1997 }
1998
1999 preventAdLoad();
2000 })();
2001
2002 document.addEventListener('DOMContentLoaded', function() {
2003
2004 function removeAdsElements() {
2005 const esfElements = document.querySelectorAll('#google_esf');
2006 esfElements.forEach(element => {
2007 element.style.display = 'none !important';
2008 if (element.parentNode) {
2009 element.parentNode.removeChild(element);
2010 }
2011 });
2012
2013 const adsSelectors = [
2014 '#adsbygoogle',
2015 '.adsbygoogle',
2016 'ins.adsbygoogle',
2017 'ins.adsbygoogle-noablate',
2018 'iframe[id^=\"google_ads_\"]',
2019 'iframe[id^=\"aswift_\"]',
2020 'div[id^=\"aswift_\"]',
2021 '[data-ad-status]',
2022 '[data-adsbygoogle-status]',
2023 '[data-google-query-id]',
2024 '[data-google-container-id]',
2025 '[data-ad-format]'
2026 ];
2027
2028 adsSelectors.forEach(selector => {
2029 const elements = document.querySelectorAll(selector);
2030 elements.forEach(element => {
2031 element.style.display = 'none !important';
2032 if (element.parentNode) {
2033 element.parentNode.removeChild(element);
2034 }
2035 });
2036 });
2037
2038 const styleElement = document.createElement('style');
2039 styleElement.textContent =
2040 '#google_esf, ' +
2041 '#adsbygoogle,' +
2042 '.adsbygoogle,' +
2043 'ins.adsbygoogle,' +
2044 'ins.adsbygoogle-noablate,' +
2045 'iframe[id^=\"google_ads_\"],' +
2046 'iframe[id^=\"aswift_\"],' +
2047 'div[id^=\"aswift_\"],' +
2048 '[data-ad-status],' +
2049 '[data-adsbygoogle-status],' +
2050 '[data-google-query-id],' +
2051 '[data-google-container-id],' +
2052 '[data-ad-format] { ' +
2053 'display: none !important; ' +
2054 'visibility: hidden !important;' +
2055 'opacity: 0 !important;' +
2056 'pointer-events: none !important;' +
2057 'width: 0px !important;' +
2058 'height: 0px !important;' +
2059 'position: absolute !important;' +
2060 'top: -9999px !important;' +
2061 'left: -9999px !important;' +
2062 '}';
2063 document.head.appendChild(styleElement);
2064 }
2065
2066 if (document.readyState === 'loading') {
2067 document.addEventListener('DOMContentLoaded', removeAdsElements);
2068 } else {
2069 removeAdsElements();
2070 }
2071
2072 const adObserver = new MutationObserver(function(mutations) {
2073 mutations.forEach(function(mutation) {
2074 if (mutation.addedNodes && mutation.addedNodes.length > 0) {
2075 for (let i = 0; i < mutation.addedNodes.length; i++) {
2076 const node = mutation.addedNodes[i];
2077 if (node.id === 'google_esf' || node.id === 'adsbygoogle') {
2078 node.style.display = 'none !important';
2079 if (node.parentNode) {
2080 node.parentNode.removeChild(node);
2081 }
2082 }
2083 }
2084 }
2085 });
2086 });
2087
2088 adObserver.observe(document.documentElement, {
2089 childList: true,
2090 subtree: true
2091 });
2092 });
2093 ";
2094
2095 wp_add_inline_script('presslearn-ad-block-js', $ad_block_script);
2096
2097 wp_register_script('presslearn-clear-blocked-flag-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
2098 wp_enqueue_script('presslearn-clear-blocked-flag-js');
2099
2100 $clear_flag_script = "
2101 (function() {
2102 try {
2103 localStorage.setItem('adsenseBlocked', 'false');
2104 localStorage.setItem('adsenseClickCount', '0');
2105 } catch (e) {}
2106 })();
2107 ";
2108
2109 wp_add_inline_script('presslearn-clear-blocked-flag-js', $clear_flag_script);
2110 }
2111
2112 add_action('wp_body_open', 'presslearn_block_ads_for_blocked_ips', 1);
2113 add_action('wp_footer', 'presslearn_check_blocked_status', 1);
2114
2115 function presslearn_check_blocked_status() {
2116 if (is_admin()) {
2117 return;
2118 }
2119
2120 $click_protection_enabled = get_option('presslearn_click_protection_enabled', 'no');
2121 if ($click_protection_enabled !== 'yes') {
2122 return;
2123 }
2124
2125 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
2126 if (empty($blocked_ips)) {
2127 return;
2128 }
2129
2130 $user_ip = presslearn_plugin()->get_visitor_ip_for_protection();
2131 $is_blocked = false;
2132
2133 foreach ($blocked_ips as $item) {
2134 if ($item['ip'] === $user_ip) {
2135 $is_blocked = true;
2136 break;
2137 }
2138 }
2139
2140 if (!$is_blocked) {
2141 return;
2142 }
2143
2144 wp_register_script('presslearn-blocked-status-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
2145 wp_enqueue_script('presslearn-blocked-status-js');
2146
2147 $blocked_status_script = "
2148 (function() {
2149
2150 function removeAds() {
2151 var selectors = [
2152 '#google_esf',
2153 '.adsbygoogle',
2154 'ins.adsbygoogle',
2155 'ins.adsbygoogle-noablate',
2156 'iframe[id^=\"google_ads_\"]',
2157 'iframe[id^=\"aswift_\"]',
2158 'div[id^=\"aswift_\"]',
2159 '[data-ad-status]',
2160 '[data-adsbygoogle-status]',
2161 '[data-google-query-id]',
2162 '[data-google-container-id]',
2163 '[data-ad-format]',
2164 'ins[class*=\"adsbygoogle\"],
2165 .right-side-rail-edge,
2166 .right-side-rail-dismiss-btn,
2167 '[data-side-rail-status]'
2168 ];
2169
2170 selectors.forEach(function(selector) {
2171 var elements = document.querySelectorAll(selector);
2172 elements.forEach(function(element) {
2173 if (element && element.parentNode) {
2174 element.parentNode.removeChild(element);
2175 }
2176 });
2177 });
2178 }
2179
2180 removeAds();
2181 setTimeout(removeAds, 1000);
2182 })();
2183 ";
2184
2185 wp_add_inline_script('presslearn-blocked-status-js', $blocked_status_script);
2186 }
2187
2188 function presslearn_add_ad_protection_script() {
2189 if (is_admin()) {
2190 return;
2191 }
2192
2193 $click_protection_enabled = get_option('presslearn_click_protection_enabled', 'no');
2194 if ($click_protection_enabled !== 'yes') {
2195 return;
2196 }
2197
2198 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
2199 $user_ip = presslearn_plugin()->get_visitor_ip_for_protection();
2200
2201 $allowed_ips = get_option('presslearn_click_protection_allowed_ips', array());
2202 $is_allowed = false;
2203
2204 foreach ($allowed_ips as $item) {
2205 if ($item['ip'] === $user_ip) {
2206 $is_allowed = true;
2207 break;
2208 }
2209 }
2210
2211 $is_blocked = false;
2212 foreach ($blocked_ips as $item) {
2213 if ($item['ip'] === $user_ip) {
2214 $is_blocked = true;
2215 break;
2216 }
2217 }
2218
2219 if ($is_allowed) {
2220 return;
2221 }
2222
2223 if ($is_blocked) {
2224 presslearn_block_ads_for_blocked_ips();
2225 return;
2226 }
2227
2228 $max_click_count = intval(get_option('presslearn_max_click_count', 10));
2229
2230 wp_register_style('presslearn-protection-modal-css', false);
2231 wp_enqueue_style('presslearn-protection-modal-css');
2232
2233 $modal_css = "
2234 .pl-modal-overlay {
2235 display: none;
2236 position: fixed;
2237 top: 0;
2238 left: 0;
2239 width: 100%;
2240 height: 100%;
2241 background-color: rgba(0, 0, 0, 0.7);
2242 z-index: 999999;
2243 justify-content: center;
2244 align-items: center;
2245 overflow: hidden;
2246 }
2247
2248 .pl-modal {
2249 background: white;
2250 padding: 30px;
2251 border-radius: 8px;
2252 max-width: 500px;
2253 width: 90%;
2254 text-align: center;
2255 box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
2256 position: relative;
2257 }
2258
2259 .pl-modal h2 {
2260 margin-top: 0;
2261 font-size: 24px;
2262 color: #d32f2f;
2263 }
2264
2265 .pl-modal p {
2266 margin: 15px 0;
2267 font-size: 16px;
2268 line-height: 1.5;
2269 }
2270
2271 .pl-modal-close {
2272 display: inline-block;
2273 background: #d32f2f;
2274 color: white;
2275 padding: 10px 20px;
2276 margin-top: 20px;
2277 border-radius: 4px;
2278 cursor: pointer;
2279 font-weight: bold;
2280 border: none;
2281 }
2282
2283 .pl-modal-close:hover {
2284 background: #b71c1c;
2285 }
2286
2287 .pl-blocked-ad-notice {
2288 display: block;
2289 padding: 15px;
2290 background-color: #fff8f8;
2291 border: 1px solid #ffdddd;
2292 text-align: center;
2293 font-size: 14px;
2294 color: #d32f2f;
2295 margin: 15px 0;
2296 border-radius: 4px;
2297 font-weight: bold;
2298 }
2299
2300 body.pl-modal-open {
2301 overflow: hidden;
2302 }
2303 ";
2304
2305 wp_add_inline_style('presslearn-protection-modal-css', $modal_css);
2306
2307 add_action('wp_footer', function() {
2308 ?>
2309 <div class="pl-modal-overlay" id="pl-block-modal">
2310 <div class="pl-modal">
2311 <h2><?php echo esc_html(get_option('presslearn_modal_title', '광고 차단 알림')); ?></h2>
2312 <p><?php echo esc_html(get_option('presslearn_modal_message', '광고 클릭 제한을 초과하여 광고가 차단되었습니다.')); ?></p>
2313 <p><?php echo esc_html(get_option('presslearn_modal_submessage', '단시간에 반복적인 광고 클릭은 시스�
2314 �에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.')); ?></p>
2315 <button class="pl-modal-close" id="pl-modal-close"><?php echo esc_html(get_option('presslearn_modal_button_text', '확인')); ?></button>
2316 </div>
2317 </div>
2318 <?php
2319 });
2320
2321 wp_register_script('presslearn-protection-script-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
2322 wp_enqueue_script('presslearn-protection-script-js');
2323
2324 $user_ip = presslearn_plugin()->get_visitor_ip_for_protection();
2325 $reset_signals = get_option('presslearn_reset_localStorage_signals', array());
2326 $should_reset = false;
2327
2328 if (isset($reset_signals[$user_ip])) {
2329 $signal = $reset_signals[$user_ip];
2330 if (time() - $signal['timestamp'] < 3600) {
2331 $should_reset = true;
2332 unset($reset_signals[$user_ip]);
2333 update_option('presslearn_reset_localStorage_signals', $reset_signals);
2334 }
2335 }
2336
2337 $protection_script = "
2338 (function() {
2339 function getStorageItem(name, defaultValue) {
2340 try {
2341 const item = localStorage.getItem(name);
2342 return item !== null ? item : defaultValue;
2343 } catch (e) {
2344 return defaultValue;
2345 }
2346 }
2347
2348 function setStorageItem(name, value) {
2349 try {
2350 localStorage.setItem(name, value);
2351 return true;
2352 } catch (e) {
2353 return false;
2354 }
2355 }
2356
2357 function resetLocalStorageForUnblock() {
2358 try {
2359 localStorage.removeItem('adsenseBlocked');
2360 localStorage.removeItem('adsenseClickCount');
2361 localStorage.removeItem('adsenseBlockedTime');
2362 localStorage.removeItem('lastClickTime');
2363
2364 setStorageItem('adsenseBlocked', 'false');
2365 setStorageItem('adsenseClickCount', '0');
2366
2367 return true;
2368 } catch (e) {
2369 return false;
2370 }
2371 }
2372
2373 " . ($should_reset ? "resetLocalStorageForUnblock();" : "") . "
2374
2375 const maxClickCount = " . esc_js($max_click_count) . ";
2376
2377 const clickCount = getStorageItem('adsenseClickCount') ?
2378 parseInt(getStorageItem('adsenseClickCount')) : 0;
2379
2380 const isBlocked = getStorageItem('adsenseBlocked') === 'true';
2381 const blockedTime = getStorageItem('adsenseBlockedTime');
2382
2383 if (isBlocked && blockedTime) {
2384 const timeDiff = Date.now() - parseInt(blockedTime);
2385 const blockExpiryDays = window.presslearn_protection_config ? window.presslearn_protection_config.block_expiry_days : 30;
2386 if (timeDiff > blockExpiryDays * 24 * 60 * 60 * 1000) {
2387 setStorageItem('adsenseBlocked', 'false');
2388 setStorageItem('adsenseClickCount', '0');
2389 }
2390 }
2391
2392 if (clickCount >= maxClickCount || isBlocked) {
2393 removeAllAds();
2394 }
2395
2396 function isAdsenseAd(element) {
2397 if (!element) return false;
2398
2399 if (element.tagName === 'IFRAME' && element.src &&
2400 element.src.includes('google')) {
2401 return true;
2402 }
2403
2404 if (element.tagName === 'INS' &&
2405 element.hasAttribute('data-ad-client') &&
2406 element.getAttribute('data-ad-client').includes('pub-')) {
2407 return true;
2408 }
2409
2410 if (element.tagName === 'DIV' &&
2411 (element.id === 'google_vignette' ||
2412 element.id === 'google_anchor' ||
2413 element.className.includes('google-auto-placed') ||
2414 element.hasAttribute('data-vignette-loaded') ||
2415 element.hasAttribute('data-anchor-status'))) {
2416 return true;
2417 }
2418
2419 if (element.tagName === 'IFRAME' &&
2420 (element.id.includes('google_ads_iframe') ||
2421 element.hasAttribute('data-google-container-id') ||
2422 (element.src && (element.src.includes('googleads') ||
2423 element.src.includes('doubleclick'))))) {
2424 return true;
2425 }
2426
2427 return false;
2428 }
2429
2430 function removeAllAds() {
2431 const selectors = [
2432 '#google_esf',
2433 '.adsbygoogle',
2434 'ins.adsbygoogle',
2435 'ins.adsbygoogle-noablate',
2436 'iframe[id^=\"google_ads_\"]',
2437 'iframe[id^=\"aswift_\"]',
2438 'div[id^=\"aswift_\"]',
2439 '[data-ad-status]',
2440 '[data-adsbygoogle-status]',
2441 '[data-google-query-id]',
2442 '[data-google-container-id]',
2443 '[data-ad-format]',
2444 '#google_vignette',
2445 '#google_anchor',
2446 '.google-auto-placed',
2447 '[data-vignette-loaded]',
2448 '[data-anchor-status]',
2449 'iframe[id*=\"google_ads_iframe\"]',
2450 'iframe[src*=\"googleads\"]',
2451 'iframe[src*=\"doubleclick\"]',
2452 'div[id^=\"google_ads_iframe\"]',
2453 'div.right-side-rail-edge',
2454 'div[data-side-rail-status]'
2455 ];
2456
2457 const style = document.createElement('style');
2458 style.textContent =
2459 '#google_esf, ' +
2460 '.adsbygoogle,' +
2461 'iframe[id^=\"google_ads_\"],' +
2462 'ins.adsbygoogle,' +
2463 'ins.adsbygoogle-noablate,' +
2464 'iframe[id^=\"aswift_\"],' +
2465 'div[id^=\"aswift_\"],' +
2466 '[data-ad-status],' +
2467 '[data-adsbygoogle-status],' +
2468 '[data-google-query-id],' +
2469 '[data-google-container-id],' +
2470 '[data-ad-format],' +
2471 '#google_vignette,' +
2472 '#google_anchor,' +
2473 '.google-auto-placed,' +
2474 '[data-vignette-loaded],' +
2475 '[data-anchor-status],' +
2476 'iframe[id*=\"google_ads_iframe\"],' +
2477 'iframe[src*=\"googleads\"],' +
2478 'iframe[src*=\"doubleclick\"],' +
2479 'div[id^=\"google_ads_iframe\"],' +
2480 'div.right-side-rail-edge,' +
2481 'div[data-side-rail-status] {' +
2482 'display: none !important;' +
2483 'visibility: hidden !important;' +
2484 'opacity: 0 !important;' +
2485 'width: 0 !important;' +
2486 'height: 0 !important;' +
2487 'position: absolute !important;' +
2488 'left: -9999px !important;' +
2489 'top: -9999px !important;' +
2490 '}';
2491 document.head.appendChild(style);
2492
2493 selectors.forEach(selector => {
2494 document.querySelectorAll(selector).forEach(element => {
2495 if (element && element.parentNode) {
2496 element.parentNode.removeChild(element);
2497 }
2498 });
2499 });
2500 }
2501
2502 function blockCurrentIP() {
2503 const formData = new FormData();
2504 formData.append('action', 'presslearn_block_current_ip');
2505 formData.append('nonce', '" . esc_js(wp_create_nonce('presslearn_block_ip_nonce')) . "');
2506
2507 fetch('" . esc_url(admin_url('admin-ajax.php')) . "', {
2508 method: 'POST',
2509 credentials: 'same-origin',
2510 body: formData
2511 });
2512 }
2513
2514 function showBlockModal() {
2515 removeAllAds();
2516
2517 const modal = document.getElementById('pl-block-modal');
2518 modal.style.display = 'flex';
2519 document.body.classList.add('pl-modal-open');
2520 }
2521
2522 document.getElementById('pl-modal-close').addEventListener('click', function() {
2523 const modal = document.getElementById('pl-block-modal');
2524 modal.style.display = 'none';
2525 document.body.classList.remove('pl-modal-open');
2526 removeAllAds();
2527
2528 setInterval(removeAllAds, 1000);
2529 });
2530
2531 function addClickCount() {
2532 let clickCount = getStorageItem('adsenseClickCount') ?
2533 parseInt(getStorageItem('adsenseClickCount')) : 0;
2534
2535 if (clickCount < maxClickCount) {
2536 clickCount++;
2537 setStorageItem('adsenseClickCount', clickCount.toString());
2538
2539 setStorageItem('lastClickTime', Date.now().toString());
2540 }
2541
2542 if (clickCount >= maxClickCount) {
2543 removeAllAds();
2544
2545 blockCurrentIP();
2546
2547 showBlockModal();
2548
2549 setStorageItem('adsenseBlocked', 'true');
2550 setStorageItem('adsenseBlockedTime', Date.now().toString());
2551 }
2552 }
2553
2554 window.addEventListener('blur', function() {
2555 const activeElement = document.activeElement;
2556
2557 if (isAdsenseAd(activeElement)) {
2558 if (window.location.href.includes('#google_vignette')) {
2559 return;
2560 }
2561
2562 addClickCount();
2563
2564 setTimeout(function() {
2565 activeElement.blur();
2566 }, 1);
2567 }
2568 });
2569
2570 document.addEventListener('click', function(e) {
2571 const target = e.target;
2572
2573 let currentElement = target;
2574 for (let i = 0; i < 5; i++) {
2575 if (!currentElement) break;
2576
2577 if (isAdsenseAd(currentElement)) {
2578 addClickCount();
2579 break;
2580 }
2581
2582 currentElement = currentElement.parentElement;
2583 }
2584 }, true);
2585
2586 window.addEventListener('message', function(event) {
2587 try {
2588 if (typeof event.data === 'string' &&
2589 (event.data.includes('google_ads') ||
2590 event.data.includes('doubleclick') ||
2591 event.data.includes('GoogleAdServingTest'))) {
2592
2593 const lastClickTime = getStorageItem('lastClickTime', '0');
2594 const now = Date.now();
2595
2596 if (now - parseInt(lastClickTime) < 2000) {
2597 addClickCount();
2598 }
2599 }
2600 } catch (e) {}
2601 });
2602 })();
2603 ";
2604
2605 wp_localize_script('presslearn-protection-script-js', 'presslearn_protection_config', array(
2606 'block_expiry_days' => get_option('presslearn_click_protection_block_expiry_days', 30)
2607 ));
2608
2609 wp_add_inline_script('presslearn-protection-script-js', $protection_script);
2610 }
2611
2612 add_action('wp_body_open', 'presslearn_add_ad_protection_script', 1);
2613
2614 function presslearn_block_current_ip_ajax() {
2615 check_ajax_referer('presslearn_block_ip_nonce', 'nonce');
2616
2617 $click_protection_enabled = get_option('presslearn_click_protection_enabled', 'no');
2618 if ($click_protection_enabled !== 'yes') {
2619 wp_send_json_error(array('message' => '애드 프로�
2620 �터가 비활성화되어 있습니다.'));
2621 wp_die();
2622 }
2623
2624 $user_ip = presslearn_plugin()->get_visitor_ip_for_protection();
2625
2626 $allowed_ips = get_option('presslearn_click_protection_allowed_ips', array());
2627 foreach ($allowed_ips as $item) {
2628 if ($item['ip'] === $user_ip) {
2629 wp_send_json_error(array('message' => '허용된 IP�
2630 니다.'));
2631 wp_die();
2632 }
2633 }
2634
2635 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
2636 $is_already_blocked = false;
2637
2638 foreach ($blocked_ips as $item) {
2639 if ($item['ip'] === $user_ip) {
2640 $is_already_blocked = true;
2641 break;
2642 }
2643 }
2644
2645 if (!$is_already_blocked) {
2646 $current_date = current_time('Y-m-d');
2647
2648 $block_expiry_days = get_option('presslearn_click_protection_block_expiry_days', 30);
2649
2650 $blocked_ips[] = array(
2651 'ip' => $user_ip,
2652 'date' => $current_date,
2653 'block_date' => $current_date,
2654 'reason' => '광고 클릭 제한 초과',
2655 'expires' => $block_expiry_days > 0 ? gmdate('Y-m-d', strtotime("+{$block_expiry_days} days")) : ''
2656 );
2657
2658 update_option('presslearn_click_protection_blocked_ips', $blocked_ips);
2659
2660 wp_send_json_success(array(
2661 'message' => 'IP가 성공적으로 차단되었습니다.',
2662 'blocked_ip' => $user_ip,
2663 'block_date' => $current_date,
2664 'is_new' => true
2665 ));
2666 } else {
2667 wp_send_json_success(array(
2668 'message' => '이미 차단된 IP�
2669 니다.',
2670 'blocked_ip' => $user_ip,
2671 'is_new' => false
2672 ));
2673 }
2674
2675 wp_die();
2676 }
2677
2678 add_action('wp_ajax_presslearn_block_current_ip', 'presslearn_block_current_ip_ajax');
2679 add_action('wp_ajax_nopriv_presslearn_block_current_ip', 'presslearn_block_current_ip_ajax');
2680
2681 function presslearn_unblock_ip_with_reset() {
2682 check_ajax_referer('presslearn_ip_nonce', 'nonce');
2683
2684 if (!current_user_can('manage_options')) {
2685 wp_send_json_error(array('message' => '권한이 없습니다.'));
2686 wp_die();
2687 }
2688
2689 $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
2690
2691 if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) {
2692 wp_send_json_error(array('message' => '유효하지 않은 IP 주소�
2693 니다.'));
2694 wp_die();
2695 }
2696
2697 $lock_key = 'presslearn_unblock_' . md5($ip);
2698 if (get_transient($lock_key)) {
2699 wp_send_json_error(array('message' => '다른 요청이 처리 중�
2700 니다. 잠시 후 다시 시도해주세요.'));
2701 wp_die();
2702 }
2703
2704 set_transient($lock_key, true, 5);
2705
2706 try {
2707 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
2708 $ip_found = false;
2709 $new_blocked_ips = array();
2710
2711 foreach ($blocked_ips as $item) {
2712 if ($item['ip'] === $ip) {
2713 $ip_found = true;
2714 } else {
2715 $new_blocked_ips[] = $item;
2716 }
2717 }
2718
2719 if (!$ip_found) {
2720 delete_transient($lock_key);
2721 wp_send_json_error(array('message' => '차단 목록에서 해당 IP를 찾을 수 없습니다.'));
2722 wp_die();
2723 }
2724
2725 update_option('presslearn_click_protection_blocked_ips_backup', $blocked_ips);
2726
2727 update_option('presslearn_click_protection_blocked_ips', $new_blocked_ips);
2728
2729 $reset_signals = get_option('presslearn_reset_localStorage_signals', array());
2730 $reset_signals[$ip] = array(
2731 'timestamp' => time(),
2732 'expires' => time() + 3600
2733 );
2734 update_option('presslearn_reset_localStorage_signals', $reset_signals);
2735
2736 delete_transient($lock_key);
2737
2738 wp_send_json_success(array(
2739 'message' => 'IP 차단이 성공적으로 해제되었습니다.',
2740 'ip' => $ip,
2741 'reset_signal_sent' => true
2742 ));
2743
2744 } catch (Exception $e) {
2745 delete_transient($lock_key);
2746 wp_send_json_error(array('message' => '서버 오류가 발생했습니다.'));
2747 }
2748
2749 wp_die();
2750 }
2751
2752 add_action('wp_ajax_presslearn_unblock_ip_with_reset', 'presslearn_unblock_ip_with_reset');
2753
2754 add_action('wp_head', 'presslearn_add_adclicker_script', 1);
2755
2756 add_action('wp_ajax_presslearn_get_blocked_logs', 'presslearn_get_blocked_logs');
2757
2758 function presslearn_get_blocked_logs() {
2759 check_ajax_referer('presslearn_ip_nonce', 'nonce');
2760
2761 if (current_user_can('manage_options')) {
2762 $blocked_ips = get_option('presslearn_click_protection_blocked_ips', array());
2763 $period = isset($_POST['period']) ? sanitize_text_field(wp_unslash($_POST['period'])) : '30';
2764
2765 if ($period !== 'all' && !empty($blocked_ips)) {
2766 $filtered_logs = array();
2767 $cutoff_date = gmdate('Y-m-d', strtotime("-{$period} days"));
2768
2769 foreach ($blocked_ips as $item) {
2770 $block_date = isset($item['block_date']) ? $item['block_date'] : $item['date'];
2771 if ($block_date >= $cutoff_date) {
2772 $filtered_logs[] = $item;
2773 }
2774 }
2775
2776 $blocked_ips = $filtered_logs;
2777 }
2778
2779 usort($blocked_ips, function($a, $b) {
2780 $date_a = isset($a['block_date']) ? $a['block_date'] : $a['date'];
2781 $date_b = isset($b['block_date']) ? $b['block_date'] : $b['date'];
2782 return strtotime($date_b) - strtotime($date_a);
2783 });
2784
2785 wp_send_json_success(array(
2786 'logs' => $blocked_ips
2787 ));
2788 }
2789
2790 wp_send_json_error(array('message' => '권한이 없습니다.'));
2791 wp_die();
2792 }
2793
2794 function presslearn_add_adclicker_script() {
2795 if (!function_exists('presslearn_plugin') || !presslearn_plugin()->is_plugin_activated()) {
2796 return;
2797 }
2798
2799 $adclicker_enabled = get_option('presslearn_ad_clicker_enabled', 'no');
2800 if ($adclicker_enabled !== 'yes') {
2801 return;
2802 }
2803
2804 if (!is_single()) {
2805 return;
2806 }
2807
2808 global $post;
2809 $post_adclicker_enabled = get_post_meta($post->ID, '_presslearn_adclicker_enabled', true);
2810 $adclicker_global_enabled = get_option('presslearn_adclicker_global_enabled', 'no');
2811
2812 if ($adclicker_global_enabled === 'yes') {
2813 } else {
2814 if ($post_adclicker_enabled === '' || $post_adclicker_enabled === 'no') {
2815 return;
2816 }
2817 }
2818
2819 $adclicker_button_text = get_post_meta($post->ID, '_presslearn_adclicker_button_text', true);
2820 if (empty($adclicker_button_text)) {
2821 $adclicker_button_text = '광고보고 콘�
2822 �츠 계속 읽기';
2823 }
2824
2825 $adclicker_ad_link = get_post_meta($post->ID, '_presslearn_adclicker_ad_link', true);
2826
2827 $adclicker_frequency = get_option('presslearn_adclicker_frequency', 'once');
2828 $adclicker_overlay_color = get_option('presslearn_adclicker_overlay_color', '#000000');
2829 $adclicker_overlay_range = get_option('presslearn_adclicker_overlay_range', 100);
2830 $adclicker_display_time = get_option('presslearn_adclicker_display_time', 'null');
2831 $adclicker_button_color = get_option('presslearn_adclicker_button_color', '#2196F3');
2832 $adclicker_button_text_color = get_option('presslearn_adclicker_button_text_color', '#ffffff');
2833
2834 wp_register_style('presslearn-adclicker-css', false);
2835 wp_enqueue_style('presslearn-adclicker-css');
2836
2837 $adclicker_css = "
2838 .pl-adclicker-overlay {
2839 display: none;
2840 position: fixed;
2841 bottom: 0;
2842 left: 0;
2843 width: 100%;
2844 height: " . esc_attr($adclicker_overlay_range) . "vh;
2845 background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 0%, " . esc_attr($adclicker_overlay_color) . " 100%);
2846 z-index: 999999;
2847 }
2848
2849 .pl-adclicker-close-button {
2850 display: none;
2851 position: fixed;
2852 bottom: 60px;
2853 left: 50%;
2854 transform: translateX(-50%);
2855 padding: 15px 30px;
2856 background-color: " . esc_attr($adclicker_button_color) . ";
2857 color: " . esc_attr($adclicker_button_text_color) . ";
2858 border: none;
2859 border-radius: 8px;
2860 font-size: 20px;
2861 font-weight: bold;
2862 cursor: pointer;
2863 z-index: 1000001;
2864 text-decoration: none;
2865 text-align: center;
2866 }
2867
2868 .pl-adclicker-close-button:hover,
2869 .pl-adclicker-close-button:focus,
2870 .pl-adclicker-close-button:active {
2871 background-color: " . esc_attr($adclicker_button_color) . ";
2872 color: " . esc_attr($adclicker_button_text_color) . ";
2873 text-decoration: none;
2874 outline: none;
2875 }
2876
2877 .pl-back-message {
2878 display: none;
2879 position: fixed;
2880 bottom: 40px;
2881 left: 50%;
2882 transform: translateX(-50%);
2883 font-size: 12px;
2884 color: " . esc_attr($adclicker_button_text_color) . ";
2885 opacity: 0.8;
2886 z-index: 1000001;
2887 }
2888
2889 @media screen and (max-width: 768px) {
2890 .pl-adclicker-close-button {
2891 width: 80%;
2892 }
2893 .pl-back-message {
2894 bottom: 30px;
2895 }
2896 }
2897
2898 .pl-countdown-label {
2899 display: none;
2900 position: absolute;
2901 top: -10px;
2902 right: -10px;
2903 width: 30px;
2904 height: 30px;
2905 border-radius: 50%;
2906 background-color: #ff3b30;
2907 color: white;
2908 font-size: 14px;
2909 font-weight: bold;
2910 text-align: center;
2911 line-height: 30px;
2912 z-index: 1000002;
2913 }
2914
2915 body.pl-adclicker-open {
2916 overflow: hidden;
2917 }
2918
2919 .presslearn-ai-icon {
2920 display: inline-block;
2921 width: 16px;
2922 height: 16px;
2923 background-image: url('" . esc_url(PRESSLEARN_PLUGIN_URL) . "assets/images/icons-meta.png');
2924 background-repeat: no-repeat;
2925 background-size: contain;
2926 vertical-align: middle;
2927 margin-left: 8px;
2928 position: relative;
2929 top: -1px;
2930 }
2931
2932 #presslearn_ai_writer .postbox-header .hndle {
2933 display: flex !important;
2934 justify-content: flex-start !important;
2935 align-items: center !important;
2936 }
2937
2938 .presslearn-ai-title-container {
2939 display: flex;
2940 align-items: center;
2941 }
2942 ";
2943
2944 wp_add_inline_style('presslearn-adclicker-css', $adclicker_css);
2945
2946 wp_register_script('presslearn-adclicker-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
2947 wp_enqueue_script('presslearn-adclicker-js');
2948
2949 $adclicker_js = "
2950 (function() {
2951 function getAdClickerStorage(name, defaultValue) {
2952 try {
2953 const item = localStorage.getItem('adclicker_' + name);
2954 return item !== null ? item : defaultValue;
2955 } catch (e) {
2956 return defaultValue;
2957 }
2958 }
2959
2960 function setAdClickerStorage(name, value) {
2961 try {
2962 localStorage.setItem('adclicker_' + name, value);
2963 return true;
2964 } catch (e) {
2965 return false;
2966 }
2967 }
2968
2969 function showAdClicker() {
2970 const overlay = document.getElementById('pl-adclicker-overlay');
2971 const closeButton = document.getElementById('pl-adclicker-close-button');
2972 const countdownLabel = document.getElementById('pl-countdown-label');
2973 const backMessage = document.getElementById('pl-back-message');
2974
2975 overlay.style.display = 'block';
2976 closeButton.style.display = 'block';
2977 backMessage.style.display = 'block';
2978 document.body.classList.add('pl-adclicker-open');
2979
2980 const displayTime = '" . esc_js($adclicker_display_time) . "';
2981 if (displayTime !== 'null') {
2982 let timeLeft = parseInt(displayTime);
2983 countdownLabel.textContent = timeLeft;
2984 countdownLabel.style.display = 'block';
2985
2986 const countdownInterval = setInterval(function() {
2987 timeLeft--;
2988 countdownLabel.textContent = timeLeft;
2989
2990 if (timeLeft <= 0) {
2991 clearInterval(countdownInterval);
2992 countdownLabel.textContent = '✕';
2993 }
2994 }, 1000);
2995 }
2996 }
2997
2998 function hideAdClicker() {
2999 const overlay = document.getElementById('pl-adclicker-overlay');
3000 const closeButton = document.getElementById('pl-adclicker-close-button');
3001 const countdownLabel = document.getElementById('pl-countdown-label');
3002 const backMessage = document.getElementById('pl-back-message');
3003
3004 overlay.style.display = 'none';
3005 closeButton.style.display = 'none';
3006 countdownLabel.style.display = 'none';
3007 backMessage.style.display = 'none';
3008 document.body.classList.remove('pl-adclicker-open');
3009
3010 const frequency = '" . esc_js($adclicker_frequency) . "';
3011 if (frequency === 'once') {
3012 setAdClickerStorage('shown', 'true');
3013 } else if (frequency === '5min') {
3014 const now = new Date().getTime();
3015 setAdClickerStorage('last_shown', now.toString());
3016 }
3017 }
3018
3019 document.addEventListener('DOMContentLoaded', function() {
3020 const closeButton = document.getElementById('pl-adclicker-close-button');
3021 if (closeButton) {
3022 closeButton.addEventListener('click', function(e) {
3023 const adLink = this.getAttribute('data-ad-link');
3024
3025 if (e.target.tagName.toLowerCase() === 'a') {
3026 hideAdClicker();
3027 } else {
3028 e.preventDefault();
3029 hideAdClicker();
3030 }
3031 });
3032 }
3033
3034 const frequency = '" . esc_js($adclicker_frequency) . "';
3035 let shouldShow = false;
3036
3037 if (frequency === 'once') {
3038 shouldShow = getAdClickerStorage('shown', '') !== 'true';
3039 } else if (frequency === '5min') {
3040 const lastShown = parseInt(getAdClickerStorage('last_shown', '0'));
3041 const now = new Date().getTime();
3042 const fiveMinutes = 5 * 60 * 1000;
3043 shouldShow = (now - lastShown) > fiveMinutes;
3044 } else if (frequency === 'loop') {
3045 shouldShow = true;
3046 }
3047
3048 if (shouldShow) {
3049 setTimeout(function() {
3050 showAdClicker();
3051 }, 1000);
3052 }
3053 });
3054 })();
3055 ";
3056
3057 wp_add_inline_script('presslearn-adclicker-js', $adclicker_js);
3058
3059 add_action('wp_footer', function() use ($adclicker_ad_link, $adclicker_button_text) {
3060 ?>
3061 <div class="pl-adclicker-overlay" id="pl-adclicker-overlay"></div>
3062 <div class="pl-adclicker-close-button" id="pl-adclicker-close-button" data-ad-link="<?php echo esc_attr($adclicker_ad_link); ?>">
3063 <?php if ($adclicker_ad_link): ?>
3064 <a href="<?php echo esc_url($adclicker_ad_link); ?>" target="_blank" style="color: inherit; text-decoration: inherit; display: block;"><?php echo esc_html($adclicker_button_text); ?></a>
3065 <?php else: ?>
3066 <?php echo esc_html($adclicker_button_text); ?>
3067 <?php endif; ?>
3068 <span class="pl-countdown-label" id="pl-countdown-label"></span>
3069 </div>
3070 <div class="pl-back-message" id="pl-back-message"><?php echo esc_html('원치않으시면 뒤로가기를 해주세요'); ?></div>
3071 <?php
3072 });
3073 }
3074
3075 add_action('wp_body_open', 'presslearn_add_adclicker_script', 1);
3076
3077
3078 function presslearn_add_adclicker_metabox() {
3079 $adclicker_enabled = get_option('presslearn_ad_clicker_enabled', 'no');
3080 if ($adclicker_enabled !== 'yes') {
3081 return;
3082 }
3083
3084 add_meta_box(
3085 'presslearn_adclicker_settings',
3086 '<span class="presslearn-ai-title-container"><span class="presslearn-ai-icon"></span>애드클리커 설정</span>',
3087 'presslearn_render_adclicker_metabox',
3088 'post',
3089 'side',
3090 'default'
3091 );
3092 }
3093 add_action('add_meta_boxes', 'presslearn_add_adclicker_metabox');
3094
3095 function presslearn_render_adclicker_metabox($post) {
3096 wp_nonce_field('presslearn_adclicker_metabox_nonce', 'presslearn_adclicker_metabox_nonce');
3097
3098 $adclicker_enabled = get_post_meta($post->ID, '_presslearn_adclicker_enabled', true);
3099 $adclicker_button_text = get_post_meta($post->ID, '_presslearn_adclicker_button_text', true);
3100 $adclicker_ad_link = get_post_meta($post->ID, '_presslearn_adclicker_ad_link', true);
3101 $adclicker_global_enabled = get_option('presslearn_adclicker_global_enabled', 'no');
3102
3103 if ($adclicker_enabled === '') {
3104 $adclicker_enabled = $adclicker_global_enabled === 'yes' ? 'yes' : 'no';
3105 }
3106
3107 if (empty($adclicker_button_text)) {
3108 $adclicker_button_text = '광고보고 콘�
3109 �츠 계속 읽기';
3110 }
3111
3112 ?>
3113 <p>
3114 <label for="presslearn_adclicker_enabled"> 게시글에서 애드클리커 동작 여부:</label><br>
3115 <select name="presslearn_adclicker_enabled" id="presslearn_adclicker_enabled" class="widefat" style="box-sizing: border-box;" <?php echo $adclicker_global_enabled === 'yes' ? 'disabled' : ''; ?>>
3116 <option value="yes" <?php selected($adclicker_enabled, 'yes'); ?>></option>
3117 <option value="no" <?php selected($adclicker_enabled, 'no'); ?>>아니오</option>
3118 </select>
3119 <?php if ($adclicker_global_enabled === 'yes'): ?>
3120 <small style="color: #666;">전체 활성화가 켜져있어 설정은 무시되고 애드클리커가 강제 활성화됩니다.</small>
3121 <?php else: ?>
3122 <small style="color: #666;">전체 활성화가 비활성화되어 있어 설정에 따라 동작합니다.</small>
3123 <?php endif; ?>
3124 </p>
3125
3126 <p>
3127 <label for="presslearn_adclicker_button_text">애드클리커 버튼 �
3128 �스트:</label><br>
3129 <input type="text" name="presslearn_adclicker_button_text" id="presslearn_adclicker_button_text"
3130 class="widefat" value="<?php echo esc_attr($adclicker_button_text); ?>">
3131 </p>
3132
3133 <p>
3134 <label for="presslearn_adclicker_ad_link">광고 링크 URL:</label><br>
3135 <input type="url" name="presslearn_adclicker_ad_link" id="presslearn_adclicker_ad_link"
3136 class="widefat" value="<?php echo esc_url($adclicker_ad_link); ?>" placeholder="https://...">
3137 <small>버튼 클릭 이동할 광고 링크</small>
3138 </p>
3139 <?php
3140 }
3141
3142 function presslearn_save_adclicker_metabox_data($post_id) {
3143 if (!isset($_POST['presslearn_adclicker_metabox_nonce'])) {
3144 return;
3145 }
3146
3147 if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['presslearn_adclicker_metabox_nonce'])), 'presslearn_adclicker_metabox_nonce')) {
3148 return;
3149 }
3150
3151 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
3152 return;
3153 }
3154
3155 if (isset($_POST['post_type']) && sanitize_text_field(wp_unslash($_POST['post_type'])) === 'post') {
3156 if (!current_user_can('edit_post', $post_id)) {
3157 return;
3158 }
3159 }
3160
3161 if (isset($_POST['presslearn_adclicker_enabled'])) {
3162 $adclicker_enabled = sanitize_text_field(wp_unslash($_POST['presslearn_adclicker_enabled']));
3163 if ($adclicker_enabled === 'yes' || $adclicker_enabled === 'no') {
3164 update_post_meta($post_id, '_presslearn_adclicker_enabled', $adclicker_enabled);
3165 }
3166 }
3167
3168 if (isset($_POST['presslearn_adclicker_button_text'])) {
3169 $adclicker_button_text = sanitize_text_field(wp_unslash($_POST['presslearn_adclicker_button_text']));
3170 update_post_meta($post_id, '_presslearn_adclicker_button_text', $adclicker_button_text);
3171 }
3172
3173 if (isset($_POST['presslearn_adclicker_ad_link'])) {
3174 $adclicker_ad_link = esc_url_raw(wp_unslash($_POST['presslearn_adclicker_ad_link']));
3175 update_post_meta($post_id, '_presslearn_adclicker_ad_link', $adclicker_ad_link);
3176 }
3177 }
3178 add_action('save_post', 'presslearn_save_adclicker_metabox_data');
3179
3180 add_action('wp_ajax_presslearn_upload_banner', 'presslearn_handle_banner_upload');
3181 add_action('wp_ajax_nopriv_presslearn_upload_banner', 'presslearn_handle_banner_upload');
3182
3183 function presslearn_handle_banner_upload() {
3184 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3185 if ($dynamic_banner_enabled !== 'yes') {
3186 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3187 wp_die();
3188 }
3189
3190 check_ajax_referer('presslearn_upload_banner_nonce', 'nonce');
3191
3192 if (!current_user_can('manage_options')) {
3193 wp_send_json_error(array('message' => '파일 �
3194 로드 권한이 없습니다.'));
3195 wp_die();
3196 }
3197
3198 if (!function_exists('wp_handle_upload')) {
3199 require_once(ABSPATH . 'wp-admin/includes/file.php');
3200 }
3201
3202 if (!isset($_FILES['banner_image'])) {
3203 wp_send_json_error(array('message' => '�
3204 로드할 파일이 없습니다.'));
3205 wp_die();
3206 }
3207
3208 $file = $_FILES['banner_image'];
3209
3210 $allowed_types = array('image/jpeg', 'image/png', 'image/gif', 'image/webp');
3211 $file_type = sanitize_mime_type($file['type']);
3212 if (!in_array($file_type, $allowed_types)) {
3213 wp_send_json_error(array('message' => '허용되지 않는 파일 형식�
3214 니다. JPG, PNG, GIF, WEBP 이미지만 �
3215 로드 가능합니다.'));
3216 wp_die();
3217 }
3218
3219 $upload_dir = wp_upload_dir();
3220
3221 $banner_type = isset($_POST['banner_type']) ? sanitize_text_field(wp_unslash($_POST['banner_type'])) : 'normal';
3222
3223 $upload_path = $upload_dir['path'];
3224 if ($banner_type === 'cover') {
3225 $upload_path = trailingslashit($upload_dir['path']) . 'cover_banners';
3226 if (!file_exists($upload_path)) {
3227 wp_mkdir_p($upload_path);
3228 }
3229 }
3230
3231 $file_name = wp_unique_filename($upload_path, sanitize_file_name($file['name']));
3232
3233 $upload_overrides = array(
3234 'test_form' => false,
3235 'test_size' => true,
3236 'test_upload' => true,
3237 'mimes' => array(
3238 'jpg|jpeg|jpe' => 'image/jpeg',
3239 'png' => 'image/png',
3240 'gif' => 'image/gif',
3241 'webp' => 'image/webp'
3242 )
3243 );
3244
3245 $movefile = wp_handle_upload($file, $upload_overrides);
3246
3247 if ($movefile && !isset($movefile['error'])) {
3248 wp_send_json_success(array(
3249 'url' => esc_url_raw($movefile['url']),
3250 'message' => '이미지가 성공적으로 �
3251 로드되었습니다.'
3252 ));
3253 } else {
3254 wp_send_json_error(array(
3255 'message' => isset($movefile['error']) ? esc_html($movefile['error']) : '파일 �
3256 로드 오류가 발생했습니다.'
3257 ));
3258 }
3259
3260 wp_die();
3261 }
3262
3263 add_action('init', 'presslearn_setup_cron_for_ip_unblock');
3264
3265 add_action('wp_ajax_presslearn_add_campaign', 'presslearn_add_campaign');
3266 add_action('wp_ajax_presslearn_get_campaigns', 'presslearn_get_campaigns');
3267 add_action('wp_ajax_presslearn_delete_campaign', 'presslearn_delete_campaign');
3268 add_action('wp_ajax_presslearn_get_campaign', 'presslearn_get_campaign');
3269 add_action('wp_ajax_presslearn_update_campaign', 'presslearn_update_campaign');
3270
3271 function presslearn_add_campaign() {
3272 if (!current_user_can('manage_options')) {
3273 wp_send_json_error(array('message' => '권한이 없습니다.'));
3274 wp_die();
3275 }
3276
3277 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3278 if ($dynamic_banner_enabled !== 'yes') {
3279 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3280 wp_die();
3281 }
3282
3283 presslearn_ensure_banners_table_exists();
3284
3285 check_ajax_referer('presslearn_campaign_nonce', 'nonce');
3286
3287 $campaign_data = isset($_POST['campaign_data']) ? wp_unslash($_POST['campaign_data']) : array();
3288
3289 if (empty($campaign_data) || !is_array($campaign_data)) {
3290 wp_send_json_error(array('message' => '유효하지 않은 캠페인 데이터�
3291 니다.'));
3292 wp_die();
3293 }
3294
3295 if (empty($campaign_data['name'])) {
3296 wp_send_json_error(array('message' => '캠페인 이름은 필수 항목�
3297 니다.'));
3298 wp_die();
3299 }
3300
3301 if ($campaign_data['type'] === 'custom') {
3302 if (empty($campaign_data['banner_url']) || empty($campaign_data['link']) ||
3303 empty($campaign_data['width']) || empty($campaign_data['height'])) {
3304 wp_send_json_error(array('message' => '배너 URL, 링크 URL, 가로/세로 크기는 필수 항목�
3305 니다.'));
3306 wp_die();
3307 }
3308 } else if ($campaign_data['type'] === 'iframe') {
3309 if (empty($campaign_data['iframe_code']) || empty($campaign_data['width']) || empty($campaign_data['height'])) {
3310 wp_send_json_error(array('message' => 'iframe 코드, 가로/세로 크기는 필수 항목�
3311 니다.'));
3312 wp_die();
3313 }
3314 } else {
3315 wp_send_json_error(array('message' => '유효하지 않은 배너 유형�
3316 니다.'));
3317 wp_die();
3318 }
3319
3320 global $wpdb;
3321 $table_name = $wpdb->prefix . 'presslearn_banners';
3322
3323 $data = array(
3324 'name' => sanitize_text_field($campaign_data['name']),
3325 'type' => sanitize_text_field($campaign_data['type']),
3326 'width' => intval($campaign_data['width']),
3327 'height' => intval($campaign_data['height']),
3328 'status' => 1,
3329 'created_at' => current_time('mysql')
3330 );
3331
3332 if ($campaign_data['type'] === 'custom') {
3333 $data['banner_url'] = esc_url_raw($campaign_data['banner_url']);
3334 $data['link'] = esc_url_raw($campaign_data['link']);
3335 if (!empty($campaign_data['cover_banner_url'])) {
3336 $data['cover_banner_url'] = esc_url_raw($campaign_data['cover_banner_url']);
3337 }
3338 } else if ($campaign_data['type'] === 'iframe') {
3339 $data['iframe_code'] = stripslashes($campaign_data['iframe_code']);
3340 if (!empty($campaign_data['link'])) {
3341 $data['link'] = esc_url_raw($campaign_data['link']);
3342 }
3343 if (!empty($campaign_data['cover_banner_url'])) {
3344 $data['cover_banner_url'] = esc_url_raw($campaign_data['cover_banner_url']);
3345 }
3346 }
3347
3348 $result = $wpdb->insert($table_name, $data);
3349
3350 if ($result === false) {
3351 wp_send_json_error(array('message' => '캠페인 추가 중 오류가 발생했습니다: ' . $wpdb->last_error));
3352 wp_die();
3353 }
3354
3355 $campaign_id = $wpdb->insert_id;
3356
3357 wp_send_json_success(array(
3358 'id' => $campaign_id,
3359 'message' => '캠페인이 성공적으로 추가되었습니다.',
3360 'shortcode' => $campaign_data['type'] === 'custom' ?
3361 '[presslearn_banner id="' . $campaign_id . '"]' :
3362 '[presslearn_iframe id="' . $campaign_id . '"]'
3363 ));
3364
3365 wp_die();
3366 }
3367
3368 function presslearn_get_campaigns() {
3369 if (!current_user_can('manage_options')) {
3370 wp_send_json_error(array('message' => '권한이 없습니다.'));
3371 wp_die();
3372 }
3373
3374 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3375 if ($dynamic_banner_enabled !== 'yes') {
3376 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3377 wp_die();
3378 }
3379
3380 presslearn_ensure_banners_table_exists();
3381
3382 check_ajax_referer('presslearn_campaign_nonce', 'nonce');
3383
3384 global $wpdb;
3385 $table_name = $wpdb->prefix . 'presslearn_banners';
3386
3387 $campaigns = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC", ARRAY_A);
3388
3389 if ($campaigns === null) {
3390 wp_send_json_error(array('message' => '캠페인 목록을 가져오는 중 오류가 발생했습니다: ' . $wpdb->last_error));
3391 wp_die();
3392 }
3393
3394 foreach ($campaigns as &$campaign) {
3395 $campaign['shortcode'] = $campaign['type'] === 'custom' ?
3396 '[presslearn_banner id="' . $campaign['id'] . '"]' :
3397 '[presslearn_iframe id="' . $campaign['id'] . '"]';
3398 }
3399
3400 wp_send_json_success(array('campaigns' => $campaigns));
3401 wp_die();
3402 }
3403
3404 function presslearn_delete_campaign() {
3405 if (!current_user_can('manage_options')) {
3406 wp_send_json_error(array('message' => '권한이 없습니다.'));
3407 wp_die();
3408 }
3409
3410 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3411 if ($dynamic_banner_enabled !== 'yes') {
3412 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3413 wp_die();
3414 }
3415
3416 presslearn_ensure_banners_table_exists();
3417
3418 check_ajax_referer('presslearn_campaign_nonce', 'nonce');
3419
3420 $campaign_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
3421
3422 if ($campaign_id <= 0) {
3423 wp_send_json_error(array('message' => '유효하지 않은 캠페인 ID�
3424 니다.'));
3425 wp_die();
3426 }
3427
3428 global $wpdb;
3429 $table_name = $wpdb->prefix . 'presslearn_banners';
3430
3431 $result = $wpdb->delete($table_name, array('id' => $campaign_id), array('%d'));
3432
3433 if ($result === false) {
3434 wp_send_json_error(array('message' => '캠페인 삭제 오류가 발생했습니다: ' . $wpdb->last_error));
3435 wp_die();
3436 }
3437
3438 wp_send_json_success(array('message' => '캠페인이 성공적으로 삭제되었습니다.'));
3439 wp_die();
3440 }
3441
3442 function presslearn_get_campaign() {
3443 if (!current_user_can('manage_options')) {
3444 wp_send_json_error(array('message' => '권한이 없습니다.'));
3445 wp_die();
3446 }
3447
3448 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3449 if ($dynamic_banner_enabled !== 'yes') {
3450 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3451 wp_die();
3452 }
3453
3454 presslearn_ensure_banners_table_exists();
3455
3456 check_ajax_referer('presslearn_campaign_nonce', 'nonce');
3457
3458 $campaign_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
3459
3460 if ($campaign_id <= 0) {
3461 wp_send_json_error(array('message' => '유효하지 않은 캠페인 ID�
3462 니다.'));
3463 wp_die();
3464 }
3465
3466 global $wpdb;
3467 $table_name = $wpdb->prefix . 'presslearn_banners';
3468
3469 $campaign = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $campaign_id), ARRAY_A);
3470
3471 if ($campaign === null) {
3472 wp_send_json_error(array('message' => '캠페인 정보를 가져오는 중 오류가 발생했습니다: ' . $wpdb->last_error));
3473 wp_die();
3474 }
3475
3476 wp_send_json_success(array('campaign' => $campaign));
3477 wp_die();
3478 }
3479
3480 function presslearn_update_campaign() {
3481 if (!current_user_can('manage_options')) {
3482 wp_send_json_error(array('message' => '권한이 없습니다.'));
3483 wp_die();
3484 }
3485
3486 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3487 if ($dynamic_banner_enabled !== 'yes') {
3488 wp_send_json_error(array('message' => '다이나믹 배너 기능이 활성화되어 있지 않습니다.'));
3489 wp_die();
3490 }
3491
3492 presslearn_ensure_banners_table_exists();
3493
3494 check_ajax_referer('presslearn_campaign_nonce', 'nonce');
3495
3496 $campaign_data = isset($_POST['campaign_data']) ? wp_unslash($_POST['campaign_data']) : array();
3497
3498 if (empty($campaign_data) || !is_array($campaign_data) || empty($campaign_data['id'])) {
3499 wp_send_json_error(array('message' => '유효하지 않은 캠페인 데이터�
3500 니다.'));
3501 wp_die();
3502 }
3503
3504 $campaign_id = intval($campaign_data['id']);
3505
3506 if (empty($campaign_data['name'])) {
3507 wp_send_json_error(array('message' => '캠페인 이름은 필수 항목�
3508 니다.'));
3509 wp_die();
3510 }
3511
3512 if ($campaign_data['type'] === 'custom') {
3513 if (empty($campaign_data['banner_url']) || empty($campaign_data['link']) ||
3514 empty($campaign_data['width']) || empty($campaign_data['height'])) {
3515 wp_send_json_error(array('message' => '배너 URL, 링크 URL, 가로/세로 크기는 필수 항목�
3516 니다.'));
3517 wp_die();
3518 }
3519 } else if ($campaign_data['type'] === 'iframe') {
3520 if (empty($campaign_data['iframe_code']) || empty($campaign_data['width']) || empty($campaign_data['height'])) {
3521 wp_send_json_error(array('message' => 'iframe 코드, 가로/세로 크기는 필수 항목�
3522 니다.'));
3523 wp_die();
3524 }
3525 } else {
3526 wp_send_json_error(array('message' => '유효하지 않은 배너 유형�
3527 니다.'));
3528 wp_die();
3529 }
3530
3531 global $wpdb;
3532 $table_name = $wpdb->prefix . 'presslearn_banners';
3533
3534 $data = array(
3535 'name' => sanitize_text_field($campaign_data['name']),
3536 'type' => sanitize_text_field($campaign_data['type']),
3537 'width' => intval($campaign_data['width']),
3538 'height' => intval($campaign_data['height']),
3539 'status' => 1,
3540 'updated_at' => current_time('mysql')
3541 );
3542
3543 if ($campaign_data['type'] === 'custom') {
3544 $data['banner_url'] = esc_url_raw($campaign_data['banner_url']);
3545 $data['link'] = esc_url_raw($campaign_data['link']);
3546 $data['iframe_code'] = '';
3547
3548 if (!empty($campaign_data['cover_banner_url'])) {
3549 $data['cover_banner_url'] = esc_url_raw($campaign_data['cover_banner_url']);
3550 } else {
3551 $data['cover_banner_url'] = '';
3552 }
3553 } else if ($campaign_data['type'] === 'iframe') {
3554 $data['iframe_code'] = stripslashes($campaign_data['iframe_code']);
3555 $data['banner_url'] = '';
3556
3557 if (!empty($campaign_data['link'])) {
3558 $data['link'] = esc_url_raw($campaign_data['link']);
3559 } else {
3560 $data['link'] = '';
3561 }
3562
3563 if (!empty($campaign_data['cover_banner_url'])) {
3564 $data['cover_banner_url'] = esc_url_raw($campaign_data['cover_banner_url']);
3565 } else {
3566 $data['cover_banner_url'] = '';
3567 }
3568 }
3569
3570 $result = $wpdb->update(
3571 $table_name,
3572 $data,
3573 array('id' => $campaign_id),
3574 array('%s', '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%s'),
3575 array('%d')
3576 );
3577
3578 if ($result === false) {
3579 wp_send_json_error(array('message' => '캠페인 수정 오류가 발생했습니다: ' . $wpdb->last_error));
3580 wp_die();
3581 }
3582
3583 wp_send_json_success(array(
3584 'message' => '캠페인이 성공적으로 수정되었습니다.',
3585 'shortcode' => $campaign_data['type'] === 'custom' ?
3586 '[presslearn_banner id="' . $campaign_id . '"]' :
3587 '[presslearn_iframe id="' . $campaign_id . '"]'
3588 ));
3589
3590 wp_die();
3591 }
3592
3593 function presslearn_register_banner_shortcodes() {
3594 add_shortcode('presslearn_banner', 'presslearn_banner_shortcode');
3595 add_shortcode('presslearn_iframe', 'presslearn_iframe_shortcode');
3596
3597 wp_register_style('presslearn-banner-styles', false);
3598 wp_enqueue_style('presslearn-banner-styles');
3599
3600 $custom_css = "
3601 .pl-dynamic-area {
3602 display: inline-block;
3603 position: relative;
3604 overflow: hidden;
3605 }
3606 .pl-dynamic-cover {
3607 position: absolute;
3608 top: 0;
3609 left: -40px;
3610 width: 100%;
3611 height: 100%;
3612 display: none;
3613 z-index: 1;
3614 touch-action: pan-x;
3615 user-select: none;
3616 cursor: grab;
3617 }
3618 .pl-dynamic-cover.enable {
3619 display: block !important;
3620 animation: sliding 1.5s ease-in-out infinite;
3621 }
3622 .pl-dynamic-cover.dragging {
3623 animation: none !important;
3624 cursor: grabbing;
3625 }
3626 .pl-dynamic-cover::after {
3627 content: attr(data-message);
3628 position: absolute;
3629 right: 15px;
3630 top: 50%;
3631 transform: translateY(-50%);
3632 background: rgba(0,0,0,0.7);
3633 color: white;
3634 padding: 8px 12px;
3635 border-radius: 20px;
3636 display: flex;
3637 align-items: center;
3638 justify-content: center;
3639 font-size: 14px;
3640 transition: opacity 0.3s;
3641 }
3642 .pl-dynamic-cover img {
3643 max-width: 100%;
3644 height: auto;
3645 }
3646 @keyframes sliding {
3647 0% {
3648 transform: translate3d(-7%, 0, 0);
3649 }
3650
3651 20% {
3652 transform: translate3d(-10%, 0, 0);
3653 }
3654
3655 40% {
3656 transform: translate3d(-5%, 0, 0);
3657 }
3658
3659 60% {
3660 transform: translate3d(-10%, 0, 0);
3661 }
3662
3663 80% {
3664 transform: translate3d(-5%, 0, 0);
3665 }
3666
3667 100% {
3668 transform: translate3d(-7%, 0, 0);
3669 }
3670 }
3671 ";
3672
3673 wp_add_inline_style('presslearn-banner-styles', $custom_css);
3674
3675 wp_register_script('presslearn-banner-script', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
3676 wp_enqueue_script('presslearn-banner-script');
3677
3678 $script = "
3679 document.addEventListener('DOMContentLoaded', function() {
3680 let startX = 0;
3681 let isDragging = false;
3682 let currentCover = null;
3683 let parentWidth = 0;
3684 let dragThreshold = false;
3685
3686 function triggerIframeClick(coverElement) {
3687 const dynamicArea = coverElement.closest('.pl-dynamic-area');
3688 if (!dynamicArea) return;
3689
3690 const customLink = dynamicArea.getAttribute('data-link');
3691 const iframeContainer = dynamicArea.querySelector('.presslearn-iframe-container iframe');
3692 const bannerLink = dynamicArea.querySelector('.pl-dynamic-area-item a');
3693
3694 if (customLink) {
3695 window.location.href = customLink;
3696 } else if (iframeContainer && iframeContainer.src) {
3697 window.location.href = iframeContainer.src;
3698 } else if (bannerLink && bannerLink.href) {
3699 try {
3700 bannerLink.click();
3701 } catch (e) {
3702 window.location.href = bannerLink.href;
3703 }
3704 }
3705
3706 coverElement.classList.remove('enable', 'dragging');
3707 coverElement.style.transform = '';
3708 }
3709
3710 window.addEventListener('blur', function() {
3711 if (currentCover && dragThreshold) {
3712 triggerIframeClick(currentCover);
3713 isDragging = false;
3714 currentCover = null;
3715 dragThreshold = false;
3716 }
3717 });
3718
3719 document.addEventListener('touchstart', function(e) {
3720 const target = e.target.closest('.pl-dynamic-cover');
3721 if (target && target.classList.contains('enable')) {
3722 e.preventDefault();
3723 startX = e.touches[0].clientX;
3724 isDragging = true;
3725 currentCover = target;
3726 parentWidth = target.parentElement.offsetWidth;
3727 dragThreshold = false;
3728
3729 currentCover.classList.add('dragging');
3730 }
3731 });
3732
3733 document.addEventListener('mousedown', function(e) {
3734 const target = e.target.closest('.pl-dynamic-cover');
3735 if (target && target.classList.contains('enable')) {
3736 e.preventDefault();
3737 startX = e.clientX;
3738 isDragging = true;
3739 currentCover = target;
3740 parentWidth = target.parentElement.offsetWidth;
3741 dragThreshold = false;
3742
3743 currentCover.classList.add('dragging');
3744 }
3745 });
3746
3747 document.addEventListener('click', function(e) {
3748 const target = e.target.closest('.pl-dynamic-cover');
3749 if (target && target.classList.contains('enable')) {
3750 e.preventDefault();
3751 triggerIframeClick(target);
3752 }
3753 });
3754
3755 document.addEventListener('touchmove', function(e) {
3756 if (!isDragging || !currentCover) return;
3757
3758 const currentX = e.touches[0].clientX;
3759 const diff = currentX - startX;
3760
3761 if (diff < 0) {
3762 currentCover.style.transform = 'translate3d(' + (diff/3) + 'px, 0, 0)';
3763
3764 const threshold = parentWidth * 0.2;
3765 if (Math.abs(diff) > threshold) {
3766 dragThreshold = true;
3767 triggerIframeClick(currentCover);
3768 isDragging = false;
3769 currentCover = null;
3770 dragThreshold = false;
3771 }
3772 }
3773 });
3774
3775 document.addEventListener('mousemove', function(e) {
3776 if (!isDragging || !currentCover) return;
3777
3778 const currentX = e.clientX;
3779 const diff = currentX - startX;
3780
3781 if (diff < 0) {
3782 currentCover.style.transform = 'translate3d(' + (diff/3) + 'px, 0, 0)';
3783
3784 const threshold = parentWidth * 0.2;
3785 if (Math.abs(diff) > threshold) {
3786 dragThreshold = true;
3787 triggerIframeClick(currentCover);
3788 isDragging = false;
3789 currentCover = null;
3790 dragThreshold = false;
3791 }
3792 }
3793 });
3794
3795 document.addEventListener('touchend', function() {
3796 if (currentCover) {
3797 currentCover.classList.remove('dragging');
3798 currentCover.style.transform = '';
3799 isDragging = false;
3800 dragThreshold = false;
3801 currentCover = null;
3802 }
3803 });
3804
3805 document.addEventListener('mouseup', function() {
3806 if (currentCover) {
3807 currentCover.classList.remove('dragging');
3808 currentCover.style.transform = '';
3809 isDragging = false;
3810 dragThreshold = false;
3811 currentCover = null;
3812 }
3813 });
3814
3815 document.addEventListener('pointerdown', function(e) {
3816 const target = e.target.closest('.pl-dynamic-cover');
3817 if (target && target.classList.contains('enable')) {
3818 setTimeout(function() {
3819 if (target.classList.contains('enable')) {
3820 dragThreshold = true;
3821 currentCover = target;
3822 }
3823 }, 100);
3824 }
3825 });
3826 });
3827 ";
3828
3829 wp_add_inline_script('presslearn-banner-script', $script);
3830 }
3831 add_action('init', 'presslearn_register_banner_shortcodes');
3832
3833 function presslearn_banner_shortcode($atts) {
3834 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3835 if ($dynamic_banner_enabled !== 'yes') {
3836 return '';
3837 }
3838
3839 presslearn_ensure_banners_table_exists();
3840
3841 $atts = shortcode_atts(array(
3842 'id' => 0,
3843 ), $atts, 'presslearn_banner');
3844
3845 $banner_id = intval($atts['id']);
3846
3847 if ($banner_id <= 0) {
3848 return '';
3849 }
3850
3851 global $wpdb;
3852 $table_name = $wpdb->prefix . 'presslearn_banners';
3853
3854 $banner = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d AND type = 'custom' AND status = 1", $banner_id), ARRAY_A);
3855
3856 if ($banner === null || empty($banner)) {
3857 return '';
3858 }
3859
3860 $banner_url = esc_url($banner['banner_url']);
3861 $cover_banner_url = !empty($banner['cover_banner_url']) ? esc_url($banner['cover_banner_url']) : '';
3862
3863 $link_url = esc_url($banner['link']);
3864 $width = intval($banner['width']);
3865 $height = intval($banner['height']);
3866
3867 if (empty($cover_banner_url)) {
3868 return '
3869 <div class="pl-dynamic-area">
3870 <div class="pl-dynamic-area-item">
3871 <a href="' . esc_url($link_url) . '" target="_blank" rel="nofollow noopener">
3872 <img src="' . esc_url($banner_url) . '" alt="' . esc_attr($banner['name']) . '" width="' . esc_attr($width) . '" height="' . esc_attr($height) . '" style="max-width:100%;height:auto;display:block;" />
3873 </a>
3874 </div>
3875 </div>
3876 ';
3877 }
3878
3879 return '
3880 <div class="pl-dynamic-area">
3881 <div class="pl-dynamic-area-item">
3882 <a href="' . esc_url($link_url) . '" target="_blank" rel="nofollow noopener">
3883 <img src="' . esc_url($banner_url) . '" alt="' . esc_attr($banner['name']) . '" width="' . esc_attr($width) . '" height="' . esc_attr($height) . '" style="max-width:100%;height:auto;display:block;" />
3884 </a>
3885 </div>
3886 <div class="pl-dynamic-cover enable" data-message="밀어서 제거">
3887 <img src="' . esc_url($cover_banner_url) . '" alt="' . esc_attr($banner['name']) . '" width="' . esc_attr($width) . '" height="' . esc_attr($height) . '" style="max-width:100%;height:auto;display:block;" draggable="true" />
3888 </div>
3889 </div>
3890 ';
3891 }
3892
3893
3894 function presslearn_iframe_shortcode($atts) {
3895 $dynamic_banner_enabled = get_option('presslearn_dynamic_banner_enabled', 'no');
3896 if ($dynamic_banner_enabled !== 'yes') {
3897 return '';
3898 }
3899
3900 presslearn_ensure_banners_table_exists();
3901
3902 $atts = shortcode_atts(array(
3903 'id' => 0,
3904 ), $atts, 'presslearn_iframe');
3905
3906 $banner_id = intval($atts['id']);
3907
3908 if ($banner_id <= 0) {
3909 return '';
3910 }
3911
3912 global $wpdb;
3913 $table_name = $wpdb->prefix . 'presslearn_banners';
3914
3915 $banner = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d AND type = 'iframe' AND status = 1", $banner_id), ARRAY_A);
3916
3917 if ($banner === null || empty($banner)) {
3918 return '';
3919 }
3920
3921 $iframe_code = $banner['iframe_code'];
3922 $width = intval($banner['width']);
3923 $height = intval($banner['height']);
3924 $link_url = !empty($banner['link']) ? esc_url($banner['link']) : '';
3925 $cover_banner_url = !empty($banner['cover_banner_url']) ? esc_url($banner['cover_banner_url']) : '';
3926
3927 $iframe_code = stripslashes($iframe_code);
3928
3929 $iframe_code = preg_replace('/width\s*=\s*(["\']?)([^"\'\s>]*)(["\']?)/', 'width="' . esc_attr($width) . 'px"', $iframe_code);
3930 $iframe_code = preg_replace('/height\s*=\s*(["\']?)([^"\'\s>]*)(["\']?)/', 'height="' . esc_attr($height) . 'px"', $iframe_code);
3931
3932 if (strpos($iframe_code, 'style=') !== false) {
3933 $iframe_code = preg_replace('/style\s*=\s*(["\'])(.*?)(["\'])/', 'style=${1}max-width:100%;${2}${3}', $iframe_code);
3934 } else {
3935 $iframe_code = str_replace('<iframe', '<iframe style="max-width:100%;"', $iframe_code);
3936 }
3937
3938 $data_link_attr = !empty($link_url) ? ' data-link="' . esc_attr($link_url) . '"' : '';
3939
3940 if (empty($cover_banner_url)) {
3941 $safe_iframe_code = presslearn_sanitize_iframe($iframe_code);
3942 return '<div class="pl-dynamic-area"' . $data_link_attr . '><div class="presslearn-iframe-container" style="width:' . esc_attr($width) . 'px;max-width:100%;margin:0 auto;">' . $safe_iframe_code . '</div></div>';
3943 }
3944
3945 $safe_iframe_code = presslearn_sanitize_iframe($iframe_code);
3946
3947 return '
3948 <div class="pl-dynamic-area"' . $data_link_attr . '>
3949 <div class="pl-dynamic-area-item">
3950 <div class="presslearn-iframe-container" style="width:' . esc_attr($width) . 'px;max-width:100%;margin:0 auto;">' . $safe_iframe_code . '</div>
3951 </div>
3952 <div class="pl-dynamic-cover enable" data-message="밀어서 제거">
3953 <img src="' . esc_url($cover_banner_url) . '" alt="' . esc_attr($banner['name']) . '" width="' . esc_attr($width) . '" height="' . esc_attr($height) . '" style="max-width:100%;height:auto;display:block;" draggable="true" />
3954 </div>
3955 </div>
3956 ';
3957 }
3958
3959 function presslearn_sanitize_iframe($iframe_code) {
3960 if (empty($iframe_code)) {
3961 return '';
3962 }
3963
3964 if (!preg_match('/<iframe[^>]*>/i', $iframe_code)) {
3965 return '';
3966 }
3967
3968 $iframe_code = preg_replace('/<script[^>]*>.*?<\/script>/is', '', $iframe_code);
3969 $iframe_code = preg_replace('/<style[^>]*>.*?<\/style>/is', '', $iframe_code);
3970 $iframe_code = preg_replace('/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', '', $iframe_code);
3971 $iframe_code = preg_replace('/\s*javascript\s*:/i', '', $iframe_code);
3972
3973 if (preg_match('/<iframe[^>]*>.*?<\/iframe>/is', $iframe_code, $matches)) {
3974 $iframe_tag = $matches[0];
3975 } else if (preg_match('/<iframe[^>]*\/?>/i', $iframe_code, $matches)) {
3976 $iframe_tag = $matches[0];
3977 if (!preg_match('/\/\s*>$/', $iframe_tag)) {
3978 $iframe_tag .= '</iframe>';
3979 }
3980 } else {
3981 return '';
3982 }
3983
3984 $trusted_domains = array(
3985 'ads-partners.coupang.com',
3986 'partner.googleadservices.com',
3987 'googleads.g.doubleclick.net',
3988 'www.googletagmanager.com',
3989 'youtube.com',
3990 'www.youtube.com',
3991 'player.vimeo.com',
3992 'cdnjs.cloudflare.com',
3993 'fonts.googleapis.com'
3994 );
3995
3996 if (preg_match('/src\s*=\s*["\']([^"\']*)["\']/', $iframe_tag, $src_matches)) {
3997 $src_url = $src_matches[1];
3998 $parsed_url = parse_url($src_url);
3999
4000 if (!empty($parsed_url['host'])) {
4001 $is_trusted = false;
4002 foreach ($trusted_domains as $trusted_domain) {
4003 if ($parsed_url['host'] === $trusted_domain ||
4004 substr($parsed_url['host'], -strlen('.' . $trusted_domain)) === '.' . $trusted_domain) {
4005 $is_trusted = true;
4006 break;
4007 }
4008 }
4009
4010 if (!$is_trusted && !current_user_can('manage_options')) {
4011 return '';
4012 }
4013 }
4014 }
4015
4016 $allowed_attributes = array(
4017 'src', 'width', 'height', 'frameborder', 'scrolling',
4018 'allowfullscreen', 'loading', 'title', 'name', 'id',
4019 'class', 'style', 'referrerpolicy', 'browsingtopics'
4020 );
4021
4022 $iframe_tag = preg_replace_callback(
4023 '/(\w+)\s*=\s*["\']([^"\']*)["\']/',
4024 function($matches) use ($allowed_attributes) {
4025 if (in_array(strtolower($matches[1]), $allowed_attributes)) {
4026 return $matches[1] . '="' . esc_attr($matches[2]) . '"';
4027 }
4028 return '';
4029 },
4030 $iframe_tag
4031 );
4032
4033 return $iframe_tag;
4034 }
4035
4036 function presslearn_add_analytics_column($columns) {
4037 $columns['presslearn_analytics'] = '<span class="dashicons dashicons-chart-bar" style="font-size: 16px; vertical-align: text-top;"></span> 통계';
4038 return $columns;
4039 }
4040 add_filter('manage_posts_columns', 'presslearn_add_analytics_column');
4041 add_filter('manage_pages_columns', 'presslearn_add_analytics_column');
4042
4043 function presslearn_analytics_column_content($column, $post_id) {
4044 if ($column !== 'presslearn_analytics') {
4045 return;
4046 }
4047
4048 global $wpdb;
4049 $permalink = get_permalink($post_id);
4050 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
4051
4052 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
4053 ?>
4054 <div class="presslearn-analytics-stats">
4055 <span class="presslearn-views">0 </span>
4056 <span class="presslearn-visitors">0 방문</span>
4057 </div>
4058 <?php
4059 return;
4060 }
4061
4062 $cache_time = get_option('presslearn_analytics_cache_time', 300);
4063 $cache_date = current_time('Y-m-d');
4064 $cache_key_visitors = "presslearn_visitors_{$post_id}_{$cache_date}";
4065 $cache_key_today = "presslearn_today_{$post_id}_{$cache_date}";
4066 $cache_key_week = "presslearn_week_{$post_id}_{$cache_date}";
4067 $cache_key_month = "presslearn_month_{$post_id}_{$cache_date}";
4068
4069 $total_views = get_post_meta($post_id, '_presslearn_post_views', true);
4070 if (empty($total_views)) {
4071 $total_views = $wpdb->get_var($wpdb->prepare(
4072 "SELECT COUNT(*) FROM $table_pageviews WHERE url = %s",
4073 $permalink
4074 ));
4075 $total_views = intval($total_views);
4076
4077 update_post_meta($post_id, '_presslearn_post_views', $total_views);
4078 }
4079
4080 $total_visitors = wp_cache_get($cache_key_visitors, 'presslearn_analytics');
4081 if (false === $total_visitors) {
4082 $total_visitors = $wpdb->get_var($wpdb->prepare(
4083 "SELECT COUNT(DISTINCT visitor_id) FROM $table_pageviews WHERE url = %s",
4084 $permalink
4085 ));
4086 $total_visitors = intval($total_visitors);
4087 wp_cache_set($cache_key_visitors, $total_visitors, 'presslearn_analytics', $cache_time);
4088 }
4089
4090 $today_views = wp_cache_get($cache_key_today, 'presslearn_analytics');
4091 if (false === $today_views) {
4092 $today_views = $wpdb->get_var($wpdb->prepare(
4093 "SELECT COUNT(*) FROM $table_pageviews WHERE url = %s AND DATE(created_at) = CURDATE()",
4094 $permalink
4095 ));
4096 $today_views = intval($today_views);
4097 wp_cache_set($cache_key_today, $today_views, 'presslearn_analytics', $cache_time);
4098 }
4099
4100 $week_views = wp_cache_get($cache_key_week, 'presslearn_analytics');
4101 if (false === $week_views) {
4102 $week_views = $wpdb->get_var($wpdb->prepare(
4103 "SELECT COUNT(*) FROM $table_pageviews WHERE url = %s AND created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)",
4104 $permalink
4105 ));
4106 $week_views = intval($week_views);
4107 wp_cache_set($cache_key_week, $week_views, 'presslearn_analytics', $cache_time);
4108 }
4109
4110 $month_views = wp_cache_get($cache_key_month, 'presslearn_analytics');
4111 if (false === $month_views) {
4112 $month_views = $wpdb->get_var($wpdb->prepare(
4113 "SELECT COUNT(*) FROM $table_pageviews WHERE url = %s AND created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)",
4114 $permalink
4115 ));
4116 $month_views = intval($month_views);
4117 wp_cache_set($cache_key_month, $month_views, 'presslearn_analytics', $cache_time);
4118 }
4119
4120 ?>
4121 <div class="presslearn-analytics-stats">
4122 <div class="presslearn-stats-row">
4123 <span class="presslearn-total">
4124 <span class="dashicons dashicons-chart-bar" style="font-size: 16px; color: #2196F3; opacity: 0.8;"></span>
4125 <?php echo esc_html(number_format($total_views)); ?>
4126 </span>
4127 </div>
4128
4129 <div class="presslearn-stats-periods">
4130 <div class="presslearn-period today" title="<?php echo esc_attr('오늘 조회수'); ?>">
4131 <span><?php echo esc_html('오늘'); ?></span> <?php echo esc_html(number_format($today_views)); ?>
4132 </div>
4133 <div class="presslearn-period week" title="<?php echo esc_attr('최근 7일 조회수'); ?>">
4134 <span><?php echo esc_html('7일'); ?></span> <?php echo esc_html(number_format($week_views)); ?>
4135 </div>
4136 <div class="presslearn-period month" title="<?php echo esc_attr('최근 30일 조회수'); ?>">
4137 <span><?php echo esc_html('30일'); ?></span> <?php echo esc_html(number_format($month_views)); ?>
4138 </div>
4139 </div>
4140 </div>
4141 <?php
4142 }
4143 add_action('manage_posts_custom_column', 'presslearn_analytics_column_content', 10, 2);
4144 add_action('manage_pages_custom_column', 'presslearn_analytics_column_content', 10, 2);
4145
4146 function presslearn_analytics_column_sortable($columns) {
4147 $columns['presslearn_analytics'] = 'presslearn_analytics';
4148 return $columns;
4149 }
4150 add_filter('manage_edit-post_sortable_columns', 'presslearn_analytics_column_sortable');
4151 add_filter('manage_edit-page_sortable_columns', 'presslearn_analytics_column_sortable');
4152
4153 function presslearn_analytics_column_orderby($query) {
4154 if (!is_admin()) {
4155 return;
4156 }
4157
4158 $orderby = $query->get('orderby');
4159
4160 if ($orderby == 'presslearn_analytics') {
4161 global $wpdb;
4162 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
4163
4164 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
4165 return;
4166 }
4167
4168 $query->set('meta_key', '_presslearn_post_views');
4169 $query->set('orderby', 'meta_value_num');
4170 }
4171 }
4172 add_action('pre_get_posts', 'presslearn_analytics_column_orderby');
4173
4174 function presslearn_schedule_post_views_update() {
4175 if (!wp_next_scheduled('presslearn_daily_post_views_update')) {
4176 wp_schedule_event(strtotime('today 00:00:00'), 'daily', 'presslearn_daily_post_views_update');
4177 }
4178 }
4179 add_action('wp', 'presslearn_schedule_post_views_update');
4180
4181 function presslearn_add_analytics_column_styles() {
4182 wp_register_style('presslearn-analytics-column-css', false);
4183 wp_enqueue_style('presslearn-analytics-column-css');
4184
4185 $analytics_column_css = "
4186 .presslearn-analytics-stats {
4187 display: block;
4188 line-height: 1.5;
4189 text-align: left;
4190 }
4191
4192 .presslearn-stats-row {
4193 display: flex;
4194 align-items: center;
4195 margin-bottom: 5px
4196 }
4197
4198 .presslearn-label {
4199 font-size: 13px;
4200 color: #666;
4201 margin-right: 5px;
4202 }
4203
4204 .presslearn-total {
4205 font-size: 16px;
4206 font-weight: bold;
4207 color: #2196F3;
4208 display: flex;
4209 align-items: center;
4210 }
4211
4212 .presslearn-total .dashicons {
4213 margin-right: 3px;
4214 }
4215
4216 .presslearn-unit {
4217 font-size: 12px;
4218 font-weight: normal;
4219 color: #666;
4220 margin-left: 1px;
4221 }
4222
4223 .presslearn-stats-periods {
4224 display: flex;
4225 flex-wrap: wrap;
4226 gap: 5px;
4227 flex-flow: column;
4228 }
4229
4230 .presslearn-period {
4231 display: flex;
4232 align-items: center;
4233 justify-content: space-between;
4234 font-size: 11px;
4235 padding: 2px 6px;
4236 border-radius: 5px;
4237 white-space: nowrap;
4238 }
4239
4240 .presslearn-period .dashicons {
4241 margin-right: 2px;
4242 }
4243
4244 .presslearn-period.today {
4245 background-color: #E8F5E9;
4246 color: #4CAF50;
4247 }
4248
4249 .presslearn-period.week {
4250 background-color: #E3F2FD;
4251 color: #2196F3;
4252 }
4253
4254 .presslearn-period.month {
4255 background-color: #EDE7F6;
4256 color: #673AB7;
4257 }
4258
4259 .column-presslearn_analytics {
4260 width: 165px;
4261 }
4262 ";
4263
4264 wp_add_inline_style('presslearn-analytics-column-css', $analytics_column_css);
4265 }
4266 add_action('admin_head', 'presslearn_add_analytics_column_styles');
4267
4268 function presslearn_update_post_views() {
4269 global $wpdb;
4270 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
4271
4272 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
4273 return;
4274 }
4275
4276 $args = array(
4277 'post_type' => array('post', 'page'),
4278 'posts_per_page' => -1,
4279 'post_status' => 'publish'
4280 );
4281 $posts = get_posts($args);
4282
4283 foreach ($posts as $post) {
4284 $permalink = get_permalink($post->ID);
4285
4286 $views = $wpdb->get_var($wpdb->prepare(
4287 "SELECT COUNT(*) FROM $table_pageviews WHERE url = %s",
4288 $permalink
4289 ));
4290
4291 update_post_meta($post->ID, '_presslearn_post_views', $views);
4292 }
4293 }
4294 add_action('presslearn_daily_post_views_update', 'presslearn_update_post_views');
4295
4296 register_activation_hook(__FILE__, 'presslearn_update_post_views');
4297
4298 function presslearn_button_animation_styles() {
4299 if (!function_exists('presslearn_plugin') || !presslearn_plugin()->is_plugin_activated()) {
4300 return;
4301 }
4302
4303 $quick_button_enabled = get_option('presslearn_quick_button_enabled', 'no');
4304 if ($quick_button_enabled !== 'yes') {
4305 return;
4306 }
4307
4308 wp_register_style('presslearn-button-animation-css', false);
4309 wp_enqueue_style('presslearn-button-animation-css');
4310
4311 $animation_styles = '
4312 .presslearn-button {
4313 transition: all 0.3s ease;
4314 }
4315
4316 @keyframes presslearn-pulse {
4317 0% { transform: scale(1); }
4318 50% { transform: scale(1.05); }
4319 100% { transform: scale(1); }
4320 }
4321
4322 .presslearn-button-animation-pulse:hover {
4323 animation: presslearn-pulse 1s infinite;
4324 }
4325
4326 .presslearn-button-animation-zoom:hover {
4327 transform: scale(1.1);
4328 }
4329
4330 .presslearn-button-animation-fade:hover {
4331 opacity: 0.8;
4332 }
4333
4334 @keyframes presslearn-shake {
4335 0%, 100% { transform: translateX(0); }
4336 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
4337 20%, 40%, 60%, 80% { transform: translateX(5px); }
4338 }
4339
4340 .presslearn-button-animation-shake:hover {
4341 animation: presslearn-shake 0.5s;
4342 }';
4343
4344 wp_add_inline_style('presslearn-button-animation-css', $animation_styles);
4345
4346 wp_register_script('presslearn-button-animation-js', false, array(), PRESSLEARN_PLUGIN_VERSION, true);
4347 wp_enqueue_script('presslearn-button-animation-js');
4348
4349 $animation_script = '
4350 document.addEventListener("DOMContentLoaded", function() {
4351 const buttons = document.querySelectorAll(".presslearn-button");
4352
4353 buttons.forEach(function(button) {
4354 const originalColor = button.style.backgroundColor;
4355 const hoverColor = button.getAttribute("data-hover-color");
4356
4357 if (hoverColor && hoverColor !== originalColor) {
4358 button.addEventListener("mouseenter", function() {
4359 this.style.backgroundColor = hoverColor;
4360 });
4361
4362 button.addEventListener("mouseleave", function() {
4363 this.style.backgroundColor = originalColor;
4364 });
4365 }
4366 });
4367 });';
4368
4369 wp_add_inline_script('presslearn-button-animation-js', $animation_script);
4370 }
4371 add_action('wp_body_open', 'presslearn_button_animation_styles');
4372
4373 function presslearn_button_animation_admin_styles() {
4374 if (!function_exists('presslearn_plugin') || !presslearn_plugin()->is_plugin_activated()) {
4375 return;
4376 }
4377
4378 $quick_button_enabled = get_option('presslearn_quick_button_enabled', 'no');
4379 if ($quick_button_enabled !== 'yes') {
4380 return;
4381 }
4382
4383 wp_register_style('presslearn-button-animation-admin-css', false);
4384 wp_enqueue_style('presslearn-button-animation-admin-css');
4385
4386 $admin_animation_styles = '
4387 .editor-styles-wrapper .presslearn-button {
4388 transition: all 0.3s ease;
4389 }
4390
4391 @keyframes presslearn-pulse {
4392 0% { transform: scale(1); }
4393 50% { transform: scale(1.05); }
4394 100% { transform: scale(1); }
4395 }
4396
4397 .editor-styles-wrapper .presslearn-button-animation-pulse:hover {
4398 animation: presslearn-pulse 1s infinite;
4399 }
4400
4401 .editor-styles-wrapper .presslearn-button-animation-zoom:hover {
4402 transform: scale(1.1);
4403 }
4404
4405 .editor-styles-wrapper .presslearn-button-animation-fade:hover {
4406 opacity: 0.8;
4407 }
4408
4409 @keyframes presslearn-shake {
4410 0%, 100% { transform: translateX(0); }
4411 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
4412 20%, 40%, 60%, 80% { transform: translateX(5px); }
4413 }
4414
4415 .editor-styles-wrapper .presslearn-button-animation-shake:hover {
4416 animation: presslearn-shake 0.5s;
4417 }';
4418
4419 wp_add_inline_style('presslearn-button-animation-admin-css', $admin_animation_styles);
4420 }
4421 add_action('admin_enqueue_scripts', 'presslearn_button_animation_admin_styles');
4422
4423 function presslearn_delete_analytics_data() {
4424 if (!current_user_can('manage_options')) {
4425 wp_die('권한이 없습니다.');
4426 }
4427
4428 check_ajax_referer('presslearn_delete_analytics_data_nonce', 'nonce');
4429
4430 global $wpdb;
4431 $table_pageviews = $wpdb->prefix . 'presslearn_pageviews';
4432 $table_visitors = $wpdb->prefix . 'presslearn_visitors';
4433 $table_referrers = $wpdb->prefix . 'presslearn_referrers';
4434
4435 $tables_exist = true;
4436
4437 if ($wpdb->get_var("SHOW TABLES LIKE '$table_pageviews'") != $table_pageviews) {
4438 $tables_exist = false;
4439 }
4440
4441 if ($wpdb->get_var("SHOW TABLES LIKE '$table_visitors'") != $table_visitors) {
4442 $tables_exist = false;
4443 }
4444
4445 if ($wpdb->get_var("SHOW TABLES LIKE '$table_referrers'") != $table_referrers) {
4446 $tables_exist = false;
4447 }
4448
4449 if (!$tables_exist) {
4450 wp_send_json_error(array('message' => '�
4451 �이블이 존재하지 않습니다.'));
4452 return;
4453 }
4454
4455 $wpdb->query("TRUNCATE TABLE $table_pageviews");
4456 $wpdb->query("TRUNCATE TABLE $table_visitors");
4457 $wpdb->query("TRUNCATE TABLE $table_referrers");
4458
4459 $wpdb->query("UPDATE $wpdb->posts SET presslearn_post_views = 0 WHERE presslearn_post_views > 0");
4460
4461 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_alpack_%'");
4462 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_alpack_%'");
4463
4464 wp_cache_flush();
4465
4466 wp_send_json_success(array('message' => '모든 통계 데이터와 캐시가 성공적으로 삭제되었습니다.'));
4467 }
4468
4469 add_action('wp_ajax_presslearn_dismiss_update_notice', 'presslearn_dismiss_update_notice');
4470
4471 function presslearn_dismiss_update_notice() {
4472 check_ajax_referer('presslearn_dismiss_notice', 'nonce');
4473
4474 if (current_user_can('manage_options')) {
4475 delete_transient('presslearn_plugin_updated_notice');
4476 wp_send_json_success();
4477 } else {
4478 wp_send_json_error('권한이 없습니다.');
4479 }
4480
4481 wp_die();
4482 }
4483
4484 add_action('wp_head', 'presslearn_output_header_code', 999);
4485 add_action('wp_body_open', 'presslearn_output_body_open_code');
4486 add_action('wp_footer', 'presslearn_output_before_closing_body_code', 1);
4487 add_action('wp_footer', 'presslearn_output_footer_code', 999);
4488
4489 /**
4490 * Output header code in <head> section
4491 *
4492 * Security Note: Code is intentionally output without escaping as this is
4493 * an admin-only feature for inserting HTML/CSS/JS code. Only users with
4494 * 'manage_options' capability can save/output code.
4495 */
4496 function presslearn_output_header_code() {
4497 if (get_option('presslearn_header_footer_enabled', 'no') !== 'yes') {
4498 return;
4499 }
4500
4501 $header_code = get_option('presslearn_header_code', '');
4502 if (!empty($header_code)) {
4503 if (current_user_can('manage_options') || !is_admin()) {
4504 echo "\n <!-- PressLearn Header Code -->\n";
4505 echo wp_unslash($header_code) . "\n";
4506 echo " <!-- /PressLearn Header Code -->\n\n";
4507 }
4508 }
4509 }
4510
4511 function presslearn_output_body_open_code() {
4512 if (get_option('presslearn_header_footer_enabled', 'no') !== 'yes') {
4513 return;
4514 }
4515
4516 $body_open_code = get_option('presslearn_body_open_code', '');
4517 if (!empty($body_open_code)) {
4518 if (current_user_can('manage_options') || !is_admin()) {
4519 echo "\n<!-- PressLearn Body Open Code -->\n";
4520 echo wp_unslash($body_open_code) . "\n";
4521 echo "<!-- /PressLearn Body Open Code -->\n\n";
4522 }
4523 }
4524 }
4525
4526 function presslearn_output_before_closing_body_code() {
4527 if (get_option('presslearn_header_footer_enabled', 'no') !== 'yes') {
4528 return;
4529 }
4530
4531 $before_closing_code = get_option('presslearn_before_closing_body_code', '');
4532 if (!empty($before_closing_code)) {
4533 if (current_user_can('manage_options') || !is_admin()) {
4534 echo "\n<!-- PressLearn Before Closing Body Code -->\n";
4535 echo wp_unslash($before_closing_code) . "\n";
4536 echo "<!-- /PressLearn Before Closing Body Code -->\n\n";
4537 }
4538 }
4539 }
4540
4541 function presslearn_output_footer_code() {
4542 if (get_option('presslearn_header_footer_enabled', 'no') !== 'yes') {
4543 return;
4544 }
4545
4546 $footer_code = get_option('presslearn_footer_code', '');
4547 if (!empty($footer_code)) {
4548 if (current_user_can('manage_options') || !is_admin()) {
4549 echo "\n<!-- PressLearn Footer Code -->\n";
4550 echo wp_unslash($footer_code) . "\n";
4551 echo "<!-- /PressLearn Footer Code -->\n\n";
4552 }
4553 }
4554 }
4555