id, 'presslearn-click-protection') === false) {
return;
}
wp_register_style(
'presslearn-protection-admin-css',
false,
array(),
PRESSLEARN_PLUGIN_VERSION
);
wp_enqueue_style('presslearn-protection-admin-css');
$protection_css = "
.country-tag-container {
display: flex;
width: 100%;
flex-wrap: wrap;
gap: 8px;
flex: 1;
min-height: 100px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
align-items: flex-start;
box-sizing: border-box;
}
.country-tag {
display: inline-flex;
align-items: center;
background: #2196F3;
color: white;
padding: 4px 10px;
border-radius: 16px;
font-size: 13px;
gap: 6px;
}
.country-tag .country-code {
background: rgba(255, 255, 255, 0.2);
padding: 2px 6px;
border-radius: 10px;
font-size: 11px;
font-weight: bold;
}
.country-tag .remove-country {
cursor: pointer;
margin-left: 4px;
font-weight: bold;
opacity: 0.8;
transition: opacity 0.2s;
}
.country-tag .remove-country:hover {
opacity: 1;
}
.country-autocomplete {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ddd;
border-top: none;
max-height: 200px;
overflow-y: auto;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 1000;
display: none;
}
.country-autocomplete-item {
padding: 10px 15px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
}
.country-autocomplete-item:hover,
.country-autocomplete-item.active {
background: #f0f0f0;
}
.country-name {
flex: 1;
}
.country-code-badge {
background: #e0e0e0;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
color: #666;
}
.ip-add-form {
position: relative;
}
#blocked-country-input {
width: 100%;
}
.presslearn-modal {
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.presslearn-modal-content {
background-color: #fff;
width: 90%;
max-width: 700px;
border-radius: 6px;
box-shadow: 0 3px 20px rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
max-height: 90vh;
}
.presslearn-modal-header {
padding: 20px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
.presslearn-modal-header h3 {
margin: 0;
font-size: 20px;
}
.presslearn-modal-close {
font-size: 28px;
font-weight: bold;
cursor: pointer;
color: #666;
}
.presslearn-modal-close:hover {
color: #000;
}
.presslearn-modal-body {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.presslearn-modal-footer {
padding: 15px 20px;
border-top: 1px solid #ddd;
text-align: right;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.settings-form-row {
margin-bottom: 20px;
}
.settings-form-row label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.settings-form-row input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.modal-preview {
margin-top: 30px;
border-top: 1px solid #eee;
padding-top: 20px;
}
.modal-preview h4 {
margin-top: 0;
margin-bottom: 15px;
}
.modal-preview-container {
border: 1px dashed #ccc;
padding: 20px;
border-radius: 5px;
}
.ip-add-form {
gap: 20px;
margin-bottom: 20px;
flex-direction: column;
}
.ip-input-group {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.ip-input-group input {
flex: 1;
}
.ip-description {
color: #666;
font-size: 13px;
margin: 5px 0 0;
}
.ip-list-table {
width: 100%;
border-collapse: collapse;
}
.ip-list-table th,
.ip-list-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.ip-list-table th {
background-color: #f9f9f9;
font-weight: 600;
}
.ip-list-table tr:hover {
background-color: #f5f5f5;
}
.delete-ip,
.delete-blocked-ip {
color: #d32f2f;
cursor: pointer;
background: none;
border: none;
padding: 0;
}
.delete-ip:hover,
.delete-blocked-ip:hover {
text-decoration: underline;
}
.log-filter {
display: flex;
gap: 10px;
margin-bottom: 20px;
align-items: center;
}
.log-list-table {
width: 100%;
border-collapse: collapse;
}
.log-list-table th,
.log-list-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.log-list-table th {
background-color: #f9f9f9;
font-weight: 600;
}
.log-list-table tr:hover {
background-color: #f5f5f5;
}
.spinner-container {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
}
.log-expired {
color: #999;
text-decoration: line-through;
}
.log-active {
color: #d32f2f;
}
";
wp_add_inline_style('presslearn-protection-admin-css', $protection_css);
}
/**
* Protection admin scripts
*/
function presslearn_protection_admin_scripts() {
if (!is_admin()) {
return;
}
$screen = get_current_screen();
if (!$screen || strpos($screen->id, 'presslearn-click-protection') === false) {
return;
}
wp_register_script(
'presslearn-protection-ip-management-js',
false,
array('jquery'),
PRESSLEARN_PLUGIN_VERSION,
true
);
wp_enqueue_script('presslearn-protection-ip-management-js');
wp_localize_script('presslearn-protection-ip-management-js', 'presslearn_protection_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('presslearn_ip_nonce')
));
$ip_management_js = "
document.addEventListener('DOMContentLoaded', function() {
let allowedIPs = [];
let blockedIPs = [];
const ipNonce = presslearn_protection_ajax.nonce;
const ajaxUrl = presslearn_protection_ajax.ajax_url;
const openAllowedBtn = document.getElementById('open-allowed-ip-editor');
const openBlockedBtn = document.getElementById('open-blocked-ip-editor');
const openBlockedCountriesBtn = document.getElementById('open-blocked-countries-editor');
if (openAllowedBtn) {
openAllowedBtn.addEventListener('click', function() {
loadAllowedIPs();
document.getElementById('allowed-ip-modal').style.display = 'flex';
});
}
if (openBlockedBtn) {
openBlockedBtn.addEventListener('click', function() {
loadBlockedIPs();
document.getElementById('blocked-ip-modal').style.display = 'flex';
});
}
if (openBlockedCountriesBtn) {
openBlockedCountriesBtn.addEventListener('click', function() {
loadBlockedCountries();
document.getElementById('blocked-countries-editor').style.display = 'flex';
});
}
document.addEventListener('click', function(e) {
if (e.target.classList.contains('presslearn-modal-close') ||
e.target.id === 'close-modal-button') {
document.getElementById('allowed-ip-modal').style.display = 'none';
}
if (e.target.classList.contains('presslearn-modal-close') ||
e.target.id === 'close-blocked-modal-button') {
document.getElementById('blocked-ip-modal').style.display = 'none';
}
if (e.target.id === 'allowed-ip-modal') {
document.getElementById('allowed-ip-modal').style.display = 'none';
}
if (e.target.id === 'blocked-ip-modal') {
document.getElementById('blocked-ip-modal').style.display = 'none';
}
});
const addIpBtn = document.getElementById('add-ip-button');
const addBlockedIpBtn = document.getElementById('add-blocked-ip-button');
const newIpInput = document.getElementById('new-ip-address');
const newBlockedIpInput = document.getElementById('new-blocked-ip');
if (addIpBtn) {
addIpBtn.addEventListener('click', addNewIP);
}
if (addBlockedIpBtn) {
addBlockedIpBtn.addEventListener('click', addNewBlockedIP);
}
if (newIpInput) {
newIpInput.addEventListener('keypress', function(e) {
if (e.which === 13) {
addNewIP();
e.preventDefault();
}
});
}
if (newBlockedIpInput) {
newBlockedIpInput.addEventListener('keypress', function(e) {
if (e.which === 13) {
addNewBlockedIP();
e.preventDefault();
}
});
}
function loadAllowedIPs() {
const tbody = document.getElementById('allowed-ip-list');
if (tbody) {
tbody.innerHTML = '
|
';
}
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_get_allowed_ips',
nonce: ipNonce
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
allowedIPs = data.data.ips || [];
renderIPTable();
} else {
alert('허용 IP 목록을 불러오는데 실패했습니다.');
allowedIPs = [];
renderIPTable();
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
allowedIPs = [];
renderIPTable();
});
}
function loadBlockedIPs() {
const tbody = document.getElementById('blocked-ip-list');
if (tbody) {
tbody.innerHTML = '
|
';
}
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_get_blocked_ips',
nonce: ipNonce
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
blockedIPs = data.data.ips || [];
renderBlockedIPTable();
} else {
alert('차단 IP 목록을 불러오는데 실패했습니다.');
blockedIPs = [];
renderBlockedIPTable();
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
blockedIPs = [];
renderBlockedIPTable();
});
}
function renderIPTable() {
const tbody = document.getElementById('allowed-ip-list');
const noIpsMessage = document.getElementById('no-ips-message');
if (!tbody) return;
tbody.innerHTML = '';
if (allowedIPs.length > 0) {
if (noIpsMessage) noIpsMessage.style.display = 'none';
allowedIPs.forEach((item) => {
const row = document.createElement('tr');
row.innerHTML = `
\${item.ip} |
\${item.date} |
|
`;
tbody.appendChild(row);
});
document.querySelectorAll('.delete-ip').forEach(btn => {
btn.addEventListener('click', function() {
const ip = this.getAttribute('data-ip');
deleteIP(ip);
});
});
} else {
if (noIpsMessage) noIpsMessage.style.display = 'block';
}
}
function renderBlockedIPTable() {
const tbody = document.getElementById('blocked-ip-list');
const noBlockedIpsMessage = document.getElementById('no-blocked-ips-message');
if (!tbody) return;
tbody.innerHTML = '';
if (blockedIPs.length > 0) {
if (noBlockedIpsMessage) noBlockedIpsMessage.style.display = 'none';
blockedIPs.forEach((item) => {
const row = document.createElement('tr');
const expiryText = item.expires ? item.expires : '영구 차단';
const reasonText = item.reason || '수동 차단';
row.innerHTML = `
\${item.ip} |
\${item.date} |
|
`;
tbody.appendChild(row);
});
document.querySelectorAll('.delete-blocked-ip').forEach(btn => {
btn.addEventListener('click', function() {
const ip = this.getAttribute('data-ip');
deleteBlockedIP(ip);
});
});
} else {
if (noBlockedIpsMessage) noBlockedIpsMessage.style.display = 'block';
}
}
function addNewIP() {
const newIpInput = document.getElementById('new-ip-address');
if (!newIpInput) return;
const newIP = newIpInput.value.trim();
if (!isValidIP(newIP)) {
alert('유효한 IP 주소를 입력해주세요.');
return;
}
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_add_allowed_ip',
nonce: ipNonce,
ip: newIP
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('IP가 성공적으로 허용 목록에 추가되었습니다.');
allowedIPs = data.data.ips;
renderIPTable();
newIpInput.value = '';
newIpInput.focus();
} else {
alert(data.data.message || '허용 IP 추가에 실패했습니다.');
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
});
}
function addNewBlockedIP() {
const newBlockedIpInput = document.getElementById('new-blocked-ip');
const permanentBlockCheckbox = document.getElementById('permanent-block-checkbox');
if (!newBlockedIpInput) return;
const newIP = newBlockedIpInput.value.trim();
const isPermanent = permanentBlockCheckbox ? permanentBlockCheckbox.checked : false;
if (!isValidIP(newIP)) {
alert('유효한 IP 주소를 입력해주세요.');
return;
}
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_add_blocked_ip',
nonce: ipNonce,
ip: newIP,
permanent: isPermanent ? '1' : '0'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('IP가 성공적으로 차단되었습니다.');
blockedIPs = data.data.ips;
renderBlockedIPTable();
newBlockedIpInput.value = '';
if (permanentBlockCheckbox) permanentBlockCheckbox.checked = false;
newBlockedIpInput.focus();
} else {
alert(data.data.message || '차단 IP 추가에 실패했습니다.');
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
});
}
function deleteIP(ip) {
if (confirm('이 IP를 허용 목록에서 삭제하시겠습니까?')) {
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_delete_allowed_ip',
nonce: ipNonce,
ip: ip
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('IP가 성공적으로 허용 목록에서 삭제되었습니다.');
allowedIPs = data.data.ips;
renderIPTable();
} else {
alert(data.data.message || '허용 IP 삭제에 실패했습니다.');
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
});
}
}
function deleteBlockedIP(ip) {
if (confirm('이 IP를 차단 목록에서 삭제하시겠습니까?')) {
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_delete_blocked_ip',
nonce: ipNonce,
ip: ip
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('IP가 성공적으로 차단 목록에서 삭제되었습니다.');
blockedIPs = data.data.ips;
renderBlockedIPTable();
} else {
alert(data.data.message || '차단 IP 삭제에 실패했습니다.');
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
});
}
}
function isValidIP(ip) {
const regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return regex.test(ip);
}
// 국가별 차단 관련 코드
const countries = [
{ name: '대한민국', code: 'KR' },
{ name: '북한', code: 'KP' },
{ name: '중국', code: 'CN' },
{ name: '일본', code: 'JP' },
{ name: '미국', code: 'US' },
{ name: '러시아', code: 'RU' },
{ name: '베트남', code: 'VN' },
{ name: '태국', code: 'TH' },
{ name: '인도네시아', code: 'ID' },
{ name: '필리핀', code: 'PH' },
{ name: '말레이시아', code: 'MY' },
{ name: '싱가포르', code: 'SG' },
{ name: '인도', code: 'IN' },
{ name: '파키스탄', code: 'PK' },
{ name: '방글라데시', code: 'BD' },
{ name: '네팔', code: 'NP' },
{ name: '스리랑카', code: 'LK' },
{ name: '몽골', code: 'MN' },
{ name: '대만', code: 'TW' },
{ name: '홍콩', code: 'HK' },
{ name: '마카오', code: 'MO' },
{ name: '캐나다', code: 'CA' },
{ name: '멕시코', code: 'MX' },
{ name: '브라질', code: 'BR' },
{ name: '아르헨티나', code: 'AR' },
{ name: '칠레', code: 'CL' },
{ name: '페루', code: 'PE' },
{ name: '콜롬비아', code: 'CO' },
{ name: '베네수엘라', code: 'VE' },
{ name: '영국', code: 'GB' },
{ name: '프랑스', code: 'FR' },
{ name: '독일', code: 'DE' },
{ name: '이탈리아', code: 'IT' },
{ name: '스페인', code: 'ES' },
{ name: '네덜란드', code: 'NL' },
{ name: '벨기에', code: 'BE' },
{ name: '스위스', code: 'CH' },
{ name: '스웨덴', code: 'SE' },
{ name: '노르웨이', code: 'NO' },
{ name: '덴마크', code: 'DK' },
{ name: '핀란드', code: 'FI' },
{ name: '폴란드', code: 'PL' },
{ name: '체코', code: 'CZ' },
{ name: '헝가리', code: 'HU' },
{ name: '루마니아', code: 'RO' },
{ name: '불가리아', code: 'BG' },
{ name: '그리스', code: 'GR' },
{ name: '터키', code: 'TR' },
{ name: '우크라이나', code: 'UA' },
{ name: '벨라루스', code: 'BY' },
{ name: '카자흐스탄', code: 'KZ' },
{ name: '우즈베키스탄', code: 'UZ' },
{ name: '이란', code: 'IR' },
{ name: '이라크', code: 'IQ' },
{ name: '사우디아라비아', code: 'SA' },
{ name: '아랍에미리트', code: 'AE' },
{ name: '이스라엘', code: 'IL' },
{ name: '이집트', code: 'EG' },
{ name: '남아프리카', code: 'ZA' },
{ name: '나이지리아', code: 'NG' },
{ name: '케냐', code: 'KE' },
{ name: '에티오피아', code: 'ET' },
{ name: '가나', code: 'GH' },
{ name: '모로코', code: 'MA' },
{ name: '알제리', code: 'DZ' },
{ name: '튀니지', code: 'TN' },
{ name: '호주', code: 'AU' },
{ name: '뉴질랜드', code: 'NZ' },
{ name: '피지', code: 'FJ' }
];
let blockedCountries = [];
let activeAutocompleteIndex = -1;
const countryInput = document.getElementById('blocked-country-input');
const countryAutocomplete = document.getElementById('country-autocomplete');
const countryTagsContainer = document.getElementById('blocked-countries-tags');
const saveBlockedCountriesBtn = document.getElementById('save-blocked-countries');
const closeCountriesModalBtn = document.getElementById('close-blocked-countries-modal');
const blockedCountriesModal = document.getElementById('blocked-countries-editor');
if (closeCountriesModalBtn) {
closeCountriesModalBtn.addEventListener('click', function() {
blockedCountriesModal.style.display = 'none';
});
}
if (blockedCountriesModal) {
blockedCountriesModal.addEventListener('click', function(e) {
if (e.target === blockedCountriesModal || e.target.classList.contains('presslearn-modal-close')) {
blockedCountriesModal.style.display = 'none';
}
});
}
function loadBlockedCountries() {
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_get_blocked_countries',
nonce: ipNonce
})
})
.then(response => response.json())
.then(data => {
if (data.success && data.data.countries) {
blockedCountries = data.data.countries;
renderCountryTags();
}
});
}
function renderCountryTags() {
countryTagsContainer.innerHTML = '';
blockedCountries.forEach(country => {
const tag = document.createElement('span');
tag.className = 'country-tag';
tag.innerHTML =
country.name +
'' + country.code + '' +
'×';
countryTagsContainer.appendChild(tag);
});
}
function showAutocomplete(searchTerm) {
const filtered = countries.filter(country =>
country.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
country.code.toLowerCase().includes(searchTerm.toLowerCase())
).filter(country =>
!blockedCountries.some(blocked => blocked.code === country.code)
);
countryAutocomplete.innerHTML = '';
activeAutocompleteIndex = -1;
if (filtered.length > 0 && searchTerm.length > 0) {
countryAutocomplete.style.display = 'block';
filtered.forEach((country, index) => {
const item = document.createElement('div');
item.className = 'country-autocomplete-item';
item.setAttribute('data-index', index);
item.innerHTML =
'' + country.name + '' +
'' + country.code + '';
item.addEventListener('click', () => selectCountry(country));
countryAutocomplete.appendChild(item);
});
} else {
countryAutocomplete.style.display = 'none';
}
}
function selectCountry(country) {
if (!blockedCountries.some(blocked => blocked.code === country.code)) {
blockedCountries.push(country);
renderCountryTags();
countryInput.value = '';
countryAutocomplete.style.display = 'none';
}
}
if (countryInput) {
countryInput.addEventListener('input', (e) => {
showAutocomplete(e.target.value);
});
countryInput.addEventListener('keydown', (e) => {
const items = countryAutocomplete.querySelectorAll('.country-autocomplete-item');
if (e.key === 'ArrowDown') {
e.preventDefault();
activeAutocompleteIndex = Math.min(activeAutocompleteIndex + 1, items.length - 1);
updateActiveItem(items);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
activeAutocompleteIndex = Math.max(activeAutocompleteIndex - 1, -1);
updateActiveItem(items);
} else if (e.key === 'Enter') {
e.preventDefault();
if (activeAutocompleteIndex >= 0 && items[activeAutocompleteIndex]) {
items[activeAutocompleteIndex].click();
}
} else if (e.key === 'Escape') {
countryAutocomplete.style.display = 'none';
activeAutocompleteIndex = -1;
}
});
countryInput.addEventListener('blur', () => {
setTimeout(() => {
countryAutocomplete.style.display = 'none';
}, 200);
});
}
function updateActiveItem(items) {
items.forEach((item, index) => {
if (index === activeAutocompleteIndex) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
if (countryTagsContainer) {
countryTagsContainer.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-country')) {
const code = e.target.getAttribute('data-code');
blockedCountries = blockedCountries.filter(country => country.code !== code);
renderCountryTags();
}
});
}
if (saveBlockedCountriesBtn) {
saveBlockedCountriesBtn.addEventListener('click', () => {
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_save_blocked_countries',
nonce: ipNonce,
countries: JSON.stringify(blockedCountries)
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('차단 국가 목록이 저장되었습니다.');
blockedCountriesModal.style.display = 'none';
} else {
alert(data.data.message || '저장에 실패했습니다.');
}
});
});
}
});
";
wp_add_inline_script('presslearn-protection-ip-management-js', $ip_management_js);
wp_register_script(
'presslearn-protection-modal-settings-js',
false,
array('jquery'),
PRESSLEARN_PLUGIN_VERSION,
true
);
wp_enqueue_script('presslearn-protection-modal-settings-js');
$modal_settings_js = "
document.addEventListener('DOMContentLoaded', function() {
const openModalSettingsBtn = document.getElementById('open-modal-settings');
const closeModalSettingsBtn = document.getElementById('close-modal-settings');
const saveModalSettingsBtn = document.getElementById('save-modal-settings');
if (openModalSettingsBtn) {
openModalSettingsBtn.addEventListener('click', function() {
document.getElementById('modal-settings-editor').style.display = 'flex';
});
}
if (closeModalSettingsBtn) {
closeModalSettingsBtn.addEventListener('click', function() {
document.getElementById('modal-settings-editor').style.display = 'none';
});
}
document.addEventListener('click', function(e) {
if (e.target.classList.contains('presslearn-modal-close')) {
document.getElementById('modal-settings-editor').style.display = 'none';
}
if (e.target.id === 'modal-settings-editor') {
document.getElementById('modal-settings-editor').style.display = 'none';
}
});
const modalTitleInput = document.getElementById('modal_title');
const modalMessageInput = document.getElementById('modal_message');
const modalSubmessageInput = document.getElementById('modal_submessage');
const modalButtonTextInput = document.getElementById('modal_button_text');
if (modalTitleInput) {
modalTitleInput.addEventListener('input', function() {
const previewTitle = document.getElementById('preview-title');
if (previewTitle) previewTitle.textContent = this.value;
const form = document.getElementById('click-protection-settings-form');
if (form) {
const hiddenInput = form.querySelector('input[name=\"modal_title\"]');
if (hiddenInput) hiddenInput.value = this.value;
}
});
}
if (modalMessageInput) {
modalMessageInput.addEventListener('input', function() {
const previewMessage = document.getElementById('preview-message');
if (previewMessage) previewMessage.textContent = this.value;
const form = document.getElementById('click-protection-settings-form');
if (form) {
const hiddenInput = form.querySelector('input[name=\"modal_message\"]');
if (hiddenInput) hiddenInput.value = this.value;
}
});
}
if (modalSubmessageInput) {
modalSubmessageInput.addEventListener('input', function() {
const previewSubmessage = document.getElementById('preview-submessage');
if (previewSubmessage) previewSubmessage.textContent = this.value;
const form = document.getElementById('click-protection-settings-form');
if (form) {
const hiddenInput = form.querySelector('input[name=\"modal_submessage\"]');
if (hiddenInput) hiddenInput.value = this.value;
}
});
}
if (modalButtonTextInput) {
modalButtonTextInput.addEventListener('input', function() {
const previewButton = document.getElementById('preview-button');
if (previewButton) previewButton.textContent = this.value;
const form = document.getElementById('click-protection-settings-form');
if (form) {
const hiddenInput = form.querySelector('input[name=\"modal_button_text\"]');
if (hiddenInput) hiddenInput.value = this.value;
}
});
}
if (saveModalSettingsBtn) {
saveModalSettingsBtn.addEventListener('click', function() {
const modalTitle = modalTitleInput ? modalTitleInput.value : '';
const modalMessage = modalMessageInput ? modalMessageInput.value : '';
const modalSubmessage = modalSubmessageInput ? modalSubmessageInput.value : '';
const modalButtonText = modalButtonTextInput ? modalButtonTextInput.value : '';
const form = document.getElementById('click-protection-settings-form');
if (form) {
const titleInput = form.querySelector('input[name=\"modal_title\"]');
const messageInput = form.querySelector('input[name=\"modal_message\"]');
const submessageInput = form.querySelector('input[name=\"modal_submessage\"]');
const buttonTextInput = form.querySelector('input[name=\"modal_button_text\"]');
if (titleInput) titleInput.value = modalTitle;
if (messageInput) messageInput.value = modalMessage;
if (submessageInput) submessageInput.value = modalSubmessage;
if (buttonTextInput) buttonTextInput.value = modalButtonText;
}
document.getElementById('modal-settings-editor').style.display = 'none';
alert('모달 설정이 적용되었습니다. 변경사항을 저장하려면 \"저장하기\" 버튼을 클릭하세요.');
});
}
});
";
wp_add_inline_script('presslearn-protection-modal-settings-js', $modal_settings_js);
wp_register_script(
'presslearn-protection-blocked-logs-js',
false,
array('jquery'),
PRESSLEARN_PLUGIN_VERSION,
true
);
wp_enqueue_script('presslearn-protection-blocked-logs-js');
wp_localize_script('presslearn-protection-blocked-logs-js', 'presslearn_logs_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('presslearn_ip_nonce')
));
$blocked_logs_js = "
document.addEventListener('DOMContentLoaded', function() {
const ajaxUrl = presslearn_logs_ajax.ajax_url;
const ipNonce = presslearn_logs_ajax.nonce;
const openLogModalBtn = document.getElementById('open-blocked-log-modal');
const closeLogModalBtn = document.getElementById('close-log-modal-button');
const refreshLogBtn = document.getElementById('refresh-log');
if (openLogModalBtn) {
openLogModalBtn.addEventListener('click', function() {
loadBlockedLogs();
document.getElementById('blocked-log-modal').style.display = 'flex';
});
}
if (closeLogModalBtn) {
closeLogModalBtn.addEventListener('click', function() {
document.getElementById('blocked-log-modal').style.display = 'none';
});
}
if (refreshLogBtn) {
refreshLogBtn.addEventListener('click', loadBlockedLogs);
}
document.addEventListener('click', function(e) {
if (e.target.classList.contains('presslearn-modal-close')) {
document.getElementById('blocked-log-modal').style.display = 'none';
}
if (e.target.id === 'blocked-log-modal') {
document.getElementById('blocked-log-modal').style.display = 'none';
}
});
function loadBlockedLogs() {
const logList = document.getElementById('blocked-log-list');
const noLogsMessage = document.getElementById('no-blocked-logs-message');
const logLoading = document.getElementById('log-loading');
const periodSelect = document.getElementById('log-filter-period');
if (logList) logList.innerHTML = '';
if (noLogsMessage) noLogsMessage.style.display = 'none';
if (logLoading) logLoading.style.display = 'block';
const period = periodSelect ? periodSelect.value : '30';
fetch(presslearn_logs_ajax.ajax_url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_get_blocked_logs',
nonce: presslearn_logs_ajax.nonce,
period: period
})
})
.then(response => response.json())
.then(data => {
if (logLoading) logLoading.style.display = 'none';
if (data.success) {
const logs = data.data.logs || [];
renderLogTable(logs);
} else {
alert('차단 로그를 불러오는데 실패했습니다.');
if (noLogsMessage) noLogsMessage.style.display = 'block';
}
})
.catch(error => {
if (logLoading) logLoading.style.display = 'none';
alert('서버 통신 오류가 발생했습니다.');
if (noLogsMessage) noLogsMessage.style.display = 'block';
});
}
function renderLogTable(logs) {
const tbody = document.getElementById('blocked-log-list');
const noLogsMessage = document.getElementById('no-blocked-logs-message');
if (!tbody) return;
tbody.innerHTML = '';
if (logs.length > 0) {
if (noLogsMessage) noLogsMessage.style.display = 'none';
const today = new Date();
logs.forEach((item) => {
let statusClass = 'log-active';
let expiryText = item.expires || '영구 차단';
let actionButton = '';
if (item.expires) {
const expiryDate = new Date(item.expires);
if (expiryDate < today) {
statusClass = 'log-expired';
expiryText += ' (만료됨)';
} else {
actionButton = ``;
}
} else {
actionButton = ``;
}
const row = document.createElement('tr');
row.className = statusClass;
row.innerHTML = `
\${item.ip} |
\${item.block_date || item.date} |
\${item.reason || '수동 차단'} |
\${expiryText} |
\${actionButton} |
`;
tbody.appendChild(row);
});
document.querySelectorAll('.unblock-ip-from-log').forEach(btn => {
btn.addEventListener('click', function() {
const ip = this.getAttribute('data-ip');
unblockIPFromLog(ip);
});
});
} else {
if (noLogsMessage) noLogsMessage.style.display = 'block';
}
}
function unblockIPFromLog(ip) {
if (confirm('이 IP의 차단을 해제하시겠습니까? 해당 IP의 브라우저 제한도 함께 해제됩니다.')) {
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'presslearn_unblock_ip_with_reset',
nonce: ipNonce,
ip: ip
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('IP 차단이 성공적으로 해제되었습니다.');
loadBlockedLogs();
if (document.getElementById('blocked-ip-modal').style.display === 'flex') {
loadBlockedIPs();
}
} else {
alert(data.data.message || 'IP 차단 해제에 실패했습니다.');
}
})
.catch(error => {
alert('서버 통신 오류가 발생했습니다.');
});
}
}
});
";
wp_add_inline_script('presslearn-protection-blocked-logs-js', $blocked_logs_js);
}
add_action('admin_enqueue_scripts', 'presslearn_protection_admin_styles');
add_action('admin_enqueue_scripts', 'presslearn_protection_admin_scripts');