807 lines
35 KiB
PHP
807 lines
35 KiB
PHP
<?php
|
||
|
||
add_action('admin_enqueue_scripts', function($hook) {
|
||
if ($hook !== 'post.php' && $hook !== 'post-new.php') return;
|
||
|
||
wp_enqueue_script('select2');
|
||
wp_enqueue_style('select2');
|
||
});
|
||
|
||
add_action('admin_footer', function () {
|
||
global $post;
|
||
|
||
// Массив с настройками для каждой таксономии
|
||
$taxonomies_settings = [
|
||
'banned' => [
|
||
'background' => '#9e5a63', // фон для banned
|
||
'color' => '#ffffff', // белый цвет для рамки
|
||
'placeholder_color' => '#ffffff', // цвет placeholder для banned
|
||
'allow_new_tags' => false // запрещаем новые теги для banned
|
||
],
|
||
'keys' => [
|
||
'background' => '#e8f2ff', // синий фон для keys
|
||
'color' => '#2271b1', // синий цвет для рамки
|
||
'placeholder_color' => '#2271b1', // цвет placeholder для keys
|
||
'allow_new_tags' => true // разрешаем новые теги для keys
|
||
],
|
||
'post_tag' => [
|
||
'background' => '#31708e', // светлый фон для post_tag
|
||
'color' => '#ffffff', // серый цвет для рамки
|
||
'placeholder_color' => '#ffffff', // цвет placeholder для post_tag
|
||
'allow_new_tags' => false // запрещаем новые теги для post_tag
|
||
]
|
||
];
|
||
?>
|
||
<script>
|
||
jQuery(function ($) {
|
||
|
||
const taxonomiesSettings = <?php echo json_encode($taxonomies_settings); ?>;
|
||
|
||
Object.keys(taxonomiesSettings).forEach(tax => {
|
||
const settings = taxonomiesSettings[tax];
|
||
|
||
// Для post_tag используется другой ID контейнера
|
||
let box;
|
||
if (tax === 'post_tag') {
|
||
box = $('#tagsdiv-post_tag');
|
||
// Если не нашли по стандартному ID, пробуем найти по классу
|
||
if (!box.length) {
|
||
box = $('.tagsdiv-post_tag');
|
||
}
|
||
} else {
|
||
box = $('#tagsdiv-' + tax);
|
||
}
|
||
|
||
if (!box.length) {
|
||
console.log('Container not found for taxonomy:', tax);
|
||
return;
|
||
}
|
||
|
||
const originalInput = box.find('.taghint').parent().find('input');
|
||
const tagList = box.find('.tagchecklist');
|
||
const tagCloudLink = box.find('.tagcloud-link');
|
||
|
||
// Получаем классы с оригинального input
|
||
const originalClasses = originalInput.attr('class') || '';
|
||
|
||
// Скрываем стандартные элементы
|
||
originalInput.hide();
|
||
box.find('.ajaxtag').hide();
|
||
tagList.hide();
|
||
|
||
// Создаем новый элемент select с классами оригинала
|
||
const select = $('<select multiple="multiple" class="' + originalClasses + '"></select>');
|
||
select.insertBefore(tagList);
|
||
|
||
// Создаем скрытое поле для хранения ID терминов
|
||
const hiddenIdsInput = $('<input type="hidden" name="' + tax + '_ids" />');
|
||
hiddenIdsInput.insertAfter(select);
|
||
|
||
// Переменная для отслеживания последнего введенного текста
|
||
let lastInputText = '';
|
||
|
||
// Функция для обновления скрытых inputs
|
||
const updateHiddenInputs = function() {
|
||
let selectedNames = [];
|
||
let selectedIds = [];
|
||
|
||
// Получаем все выбранные значения из Select2
|
||
const selectedData = select.select2('data');
|
||
|
||
selectedData.forEach(item => {
|
||
const value = item.id;
|
||
const text = item.text;
|
||
|
||
selectedNames.push(text);
|
||
|
||
// Для новых тегов используем название вместо ID
|
||
if (value.toString().startsWith('NEW_')) {
|
||
// Сохраняем название тега (без префикса NEW_)
|
||
selectedIds.push(text);
|
||
} else {
|
||
// Существующий ID термина
|
||
selectedIds.push(value);
|
||
}
|
||
});
|
||
|
||
// Для совместимости со стандартным input WordPress
|
||
originalInput.val(selectedNames.join(','));
|
||
// Для нашего обработчика сохранения
|
||
hiddenIdsInput.val(selectedIds.join(','));
|
||
|
||
console.log('Updated hidden inputs for', tax, ':', {
|
||
names: selectedNames.join(','),
|
||
ids: selectedIds.join(',')
|
||
});
|
||
};
|
||
|
||
// Функция для принудительного добавления тега
|
||
const forceAddTag = function(tagText) {
|
||
if (!tagText || tagText.trim() === '') return false;
|
||
|
||
const trimmedText = tagText.trim();
|
||
const newTagId = 'NEW_' + trimmedText;
|
||
|
||
// Проверяем, нет ли уже такого тега в выбранных
|
||
const existingOptions = select.find('option');
|
||
let alreadyExists = false;
|
||
|
||
existingOptions.each(function() {
|
||
if ($(this).val() === newTagId || $(this).text().toLowerCase() === trimmedText.toLowerCase()) {
|
||
alreadyExists = true;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if (!alreadyExists && settings.allow_new_tags) {
|
||
const option = new Option(trimmedText, newTagId, true, true);
|
||
select.append(option);
|
||
select.trigger('change');
|
||
|
||
console.log('Force added tag:', trimmedText);
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
// Настройки Select2 в зависимости от таксономии
|
||
const select2Options = {
|
||
ajax: {
|
||
url: ajaxurl,
|
||
dataType: 'json',
|
||
delay: 250,
|
||
data: (params) => ({
|
||
action: 'get_existing_terms',
|
||
taxonomy: tax,
|
||
search: params.term || ''
|
||
}),
|
||
processResults: function (data) {
|
||
return {
|
||
results: data
|
||
};
|
||
}
|
||
},
|
||
placeholder: '',
|
||
allowClear: true,
|
||
width: '100%',
|
||
minimumInputLength: 1, // Минимальная длина для поиска
|
||
matcher: function(params, data) {
|
||
// Если нет поискового запроса, показываем все результаты
|
||
if ($.trim(params.term) === '') {
|
||
return data;
|
||
}
|
||
|
||
// Проверяем, содержит ли текст данные поискового запроса
|
||
if (data.text.toLowerCase().indexOf(params.term.toLowerCase()) > -1) {
|
||
return data;
|
||
}
|
||
|
||
// Если не нашли совпадение, скрываем результат
|
||
return null;
|
||
}
|
||
};
|
||
|
||
// Для banned и post_tag - только выбор из существующих
|
||
// Для keys - разрешаем создание новых тегов
|
||
if (settings.allow_new_tags) {
|
||
select2Options.tags = true;
|
||
select2Options.createTag = function (params) {
|
||
var term = $.trim(params.term);
|
||
|
||
if (term === '') {
|
||
return null;
|
||
}
|
||
|
||
// Проверяем, есть ли уже такой термин в результатах поиска
|
||
// Если есть - не создаем новый тег
|
||
return {
|
||
id: 'NEW_' + term,
|
||
text: term,
|
||
newTag: true
|
||
};
|
||
};
|
||
|
||
// Добавляем валидацию для новых тегов
|
||
select2Options.insertTag = function (data, tag) {
|
||
// Проверяем, не существует ли уже такого тега в данных
|
||
const existingTag = data.find(item =>
|
||
item.text.toLowerCase() === tag.text.toLowerCase()
|
||
);
|
||
|
||
if (existingTag) {
|
||
// Если тег уже существует, не добавляем новый
|
||
return data;
|
||
}
|
||
|
||
// Добавляем новый тег в начало списка
|
||
data.unshift(tag);
|
||
return data;
|
||
};
|
||
} else {
|
||
select2Options.tags = false;
|
||
// Для таксономий без создания новых тегов запрещаем любые пользовательские вводы
|
||
select2Options.createTag = function (params) {
|
||
return null;
|
||
};
|
||
}
|
||
|
||
// Инициализация select2
|
||
select.select2(select2Options);
|
||
|
||
// Обработчики для гарантированного добавления тегов
|
||
if (settings.allow_new_tags) {
|
||
// Обработчик ввода - сохраняем последний введенный текст
|
||
select.on('input', '.select2-search__field', function(e) {
|
||
lastInputText = $(this).val();
|
||
});
|
||
|
||
// Обработчик keydown на самом select элементе
|
||
select.on('keydown', function(e) {
|
||
if (e.which === 13) { // Enter
|
||
const select2Container = select.next('.select2-container');
|
||
const searchInput = select2Container.find('.select2-search__field');
|
||
const currentText = searchInput.val().trim();
|
||
|
||
if (currentText !== '') {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
|
||
// Даем немного времени Select2 на обработку
|
||
setTimeout(() => {
|
||
if (forceAddTag(currentText)) {
|
||
searchInput.val('');
|
||
updateHiddenInputs();
|
||
}
|
||
}, 50);
|
||
}
|
||
}
|
||
});
|
||
|
||
// Дополнительный обработчик на document для перехвата всех Enter
|
||
$(document).on('keydown', function(e) {
|
||
if (e.which === 13) {
|
||
const activeElement = document.activeElement;
|
||
if ($(activeElement).hasClass('select2-search__field')) {
|
||
const select2Container = $(activeElement).closest('.select2-container');
|
||
const relatedSelect = $('select').filter(function() {
|
||
return $(this).next('.select2-container').is(select2Container);
|
||
});
|
||
|
||
if (relatedSelect.length && settings.allow_new_tags) {
|
||
const currentText = $(activeElement).val().trim();
|
||
if (currentText !== '') {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
|
||
setTimeout(() => {
|
||
if (forceAddTag(currentText)) {
|
||
$(activeElement).val('');
|
||
updateHiddenInputs();
|
||
}
|
||
}, 50);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Обработчик для случаев, когда Select2 не успевает обработать тег
|
||
select.on('select2:closing', function(e) {
|
||
const select2Container = select.next('.select2-container');
|
||
const searchInput = select2Container.find('.select2-search__field');
|
||
const currentText = searchInput.val().trim();
|
||
|
||
if (currentText !== '') {
|
||
setTimeout(() => {
|
||
if (forceAddTag(currentText)) {
|
||
searchInput.val('');
|
||
updateHiddenInputs();
|
||
}
|
||
}, 100);
|
||
}
|
||
});
|
||
}
|
||
|
||
// Обработчики событий Select2
|
||
select.on('change', function () {
|
||
console.log('Select2 changed for', tax, 'selected values:', $(this).val());
|
||
updateHiddenInputs();
|
||
});
|
||
|
||
select.on('select2:select', function (e) {
|
||
console.log('Select2 select for', tax, 'selected item:', e.params.data);
|
||
updateHiddenInputs();
|
||
});
|
||
|
||
select.on('select2:unselect', function (e) {
|
||
console.log('Select2 unselect for', tax, 'unselected item:', e.params.data);
|
||
updateHiddenInputs();
|
||
});
|
||
|
||
// Загружаем выбранные термы в select2
|
||
$.ajax({
|
||
url: ajaxurl,
|
||
type: 'POST',
|
||
dataType: 'json',
|
||
data: {
|
||
action: 'get_post_terms',
|
||
post_id: <?php echo (int)$post->ID; ?>,
|
||
taxonomy: tax
|
||
}
|
||
}).done(function (terms) {
|
||
terms.forEach(t => {
|
||
let option = new Option(t.text, t.id, true, true);
|
||
select.append(option);
|
||
});
|
||
select.trigger('change');
|
||
|
||
// Обновляем скрытые inputs
|
||
updateHiddenInputs();
|
||
});
|
||
|
||
// Переменная для хранения текущего облака
|
||
let currentTagCloud = null;
|
||
|
||
// Обработчик клика по ссылке "Выбрать из часто используемых меток"
|
||
tagCloudLink.off('click').on('click', function(e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
|
||
// Если облако уже открыто - закрываем его
|
||
if (currentTagCloud && currentTagCloud.is(':visible')) {
|
||
currentTagCloud.remove();
|
||
currentTagCloud = null;
|
||
return;
|
||
}
|
||
|
||
// Удаляем предыдущее облако если есть
|
||
if (currentTagCloud) {
|
||
currentTagCloud.remove();
|
||
}
|
||
|
||
// Загружаем популярные теги через AJAX
|
||
$.ajax({
|
||
url: ajaxurl,
|
||
type: 'GET',
|
||
dataType: 'json',
|
||
data: {
|
||
action: 'get_popular_terms',
|
||
taxonomy: tax
|
||
}
|
||
}).done(function(terms) {
|
||
// Создаем свое облако тегов в стандартном стиле WordPress
|
||
if (terms.length > 0) {
|
||
currentTagCloud = $('<div class="the-tagcloud" style="margin-top: 10px;"></div>');
|
||
|
||
// Находим минимальное и максимальное количество использования
|
||
let minCount = Math.min(...terms.map(term => term.count));
|
||
let maxCount = Math.max(...terms.map(term => term.count));
|
||
|
||
terms.forEach(term => {
|
||
const tagLink = $('<a href="#" class="tag-cloud-link"></a>');
|
||
tagLink.text(term.name);
|
||
tagLink.attr('data-id', term.id);
|
||
|
||
// Рассчитываем размер шрифта на основе популярности
|
||
const fontSize = calculateFontSize(term.count, minCount, maxCount);
|
||
tagLink.css('font-size', fontSize + 'px');
|
||
|
||
currentTagCloud.append(tagLink);
|
||
currentTagCloud.append(' ');
|
||
});
|
||
|
||
box.append(currentTagCloud);
|
||
|
||
// Обработчик клика по тегам в нашем облаке
|
||
currentTagCloud.on('click', 'a', function(e) {
|
||
e.preventDefault();
|
||
const tagName = $(this).text();
|
||
const tagId = $(this).data('id');
|
||
|
||
// Добавляем тег в Select2
|
||
if (!select.find('option[value="' + tagId + '"]').length) {
|
||
let option = new Option(tagName, tagId, true, true);
|
||
select.append(option);
|
||
select.trigger('change');
|
||
updateHiddenInputs();
|
||
}
|
||
|
||
// Закрываем облако после выбора
|
||
currentTagCloud.remove();
|
||
currentTagCloud = null;
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// Функция для расчета размера шрифта на основе популярности
|
||
function calculateFontSize(count, minCount, maxCount) {
|
||
// Минимальный и максимальный размер шрифта в px
|
||
const minSize = 11;
|
||
const maxSize = 18;
|
||
|
||
// Если все теги имеют одинаковую популярность
|
||
if (minCount === maxCount) {
|
||
return (minSize + maxSize) / 2;
|
||
}
|
||
|
||
// Рассчитываем размер пропорционально популярности
|
||
const scale = (count - minCount) / (maxCount - minCount);
|
||
return minSize + (scale * (maxSize - minSize));
|
||
}
|
||
|
||
// Закрываем облако при клике вне его
|
||
$(document).on('click', function(e) {
|
||
if (currentTagCloud && !$(e.target).closest('.the-tagcloud').length && !$(e.target).closest('.tagcloud-link').length) {
|
||
currentTagCloud.remove();
|
||
currentTagCloud = null;
|
||
}
|
||
});
|
||
|
||
});
|
||
|
||
$('.tagcloud-link').filter(function() {
|
||
return $(this).text().trim() === 'Выбрать из часто используемых меток';
|
||
}).text('Выбрать из часто используемых');
|
||
|
||
});
|
||
</script>
|
||
|
||
<!-- Стили с фоном select, цветными рамками и placeholder -->
|
||
<style>
|
||
.select2-container {
|
||
margin-bottom: 8px;
|
||
width: 100% !important;
|
||
}
|
||
.select2-selection {
|
||
min-height: 30px;
|
||
}
|
||
|
||
/* Стили для banned */
|
||
#tagsdiv-banned .select2-container--default .select2-selection--multiple {
|
||
background-color: <?php echo $taxonomies_settings['banned']['background']; ?> !important;
|
||
}
|
||
#tagsdiv-banned .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['banned']['background']; ?> !important;
|
||
color: #ffffff !important;
|
||
border-color: <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
}
|
||
#tagsdiv-banned .select2-container--default.select2-container--focus .select2-selection--multiple {
|
||
border-color: <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
}
|
||
/* Placeholder для banned */
|
||
#tagsdiv-banned .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
|
||
color: <?php echo $taxonomies_settings['banned']['placeholder_color']; ?> !important;
|
||
opacity: 0.8;
|
||
}
|
||
/* Курсор для banned */
|
||
#tagsdiv-banned .select2-container--default .select2-search--inline .select2-search__field {
|
||
color: <?php echo $taxonomies_settings['banned']['placeholder_color']; ?> !important;
|
||
caret-color: <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
}
|
||
#tagsdiv-banned .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['banned']['background']; ?> !important;
|
||
color: #ffffff !important;
|
||
border-color: <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
}
|
||
|
||
/* Стили для keys */
|
||
#tagsdiv-keys .select2-container--default .select2-selection--multiple {
|
||
background-color: <?php echo $taxonomies_settings['keys']['background']; ?> !important;
|
||
}
|
||
#tagsdiv-keys .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['keys']['background']; ?> !important;
|
||
color: #2271b1 !important;
|
||
border-color: <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
}
|
||
#tagsdiv-keys .select2-container--default.select2-container--focus .select2-selection--multiple {
|
||
border-color: <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
}
|
||
/* Placeholder для keys */
|
||
#tagsdiv-keys .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
|
||
color: <?php echo $taxonomies_settings['keys']['placeholder_color']; ?> !important;
|
||
opacity: 0.8;
|
||
}
|
||
/* Курсор для keys */
|
||
#tagsdiv-keys .select2-container--default .select2-search--inline .select2-search__field {
|
||
color: <?php echo $taxonomies_settings['keys']['placeholder_color']; ?> !important;
|
||
caret-color: <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
}
|
||
#tagsdiv-keys .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['keys']['background']; ?> !important;
|
||
color: #2271b1 !important;
|
||
border-color: <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
}
|
||
|
||
/* Стили для post_tag */
|
||
#tagsdiv-post_tag .select2-container--default .select2-selection--multiple,
|
||
.tagsdiv-post_tag .select2-container--default .select2-selection--multiple {
|
||
background-color: <?php echo $taxonomies_settings['post_tag']['background']; ?> !important;
|
||
}
|
||
#tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__choice,
|
||
.tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['post_tag']['background']; ?> !important;
|
||
color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
border-color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
} !impor
|
||
#tagsdiv-post_tag .select2-container--default.select2-container--focus .select2-selection--multiple,
|
||
.tagsdiv-post_tag .select2-container--default.select2-container--focus .select2-selection--multiple {
|
||
border-color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
}
|
||
/* Placeholder для post_tag */
|
||
#tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__placeholder,
|
||
.tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
|
||
color: <?php echo $taxonomies_settings['post_tag']['placeholder_color']; ?> !important;
|
||
opacity: 0.8;
|
||
}
|
||
/* Курсор для post_tag */
|
||
#tagsdiv-post_tag .select2-container--default .select2-search--inline .select2-search__field,
|
||
.tagsdiv-post_tag .select2-container--default .select2-search--inline .select2-search__field {
|
||
color: <?php echo $taxonomies_settings['post_tag']['placeholder_color']; ?> !important;
|
||
caret-color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
}
|
||
#tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__choice,
|
||
.tagsdiv-post_tag .select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||
background-color: <?php echo $taxonomies_settings['post_tag']['background']; ?> !important;
|
||
color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
border-color: <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
}
|
||
|
||
/* Общие стили для кнопок удаления */
|
||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||
color: inherit !important;
|
||
opacity: 0.7;
|
||
}
|
||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||
opacity: 1;
|
||
color: inherit !important;
|
||
}
|
||
|
||
/* Стили для текста в поле ввода (для всех таксономий) */
|
||
.select2-container--default .select2-search--inline .select2-search__field {
|
||
background: transparent !important;
|
||
margin-top: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
/* Стили для облака тегов в стандартном стиле WordPress */
|
||
.the-tagcloud {
|
||
background: #fff;
|
||
border: 1px solid #ccd0d4;
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
line-height: 2;
|
||
}
|
||
.the-tagcloud a {
|
||
color: #2271b1 !important;
|
||
text-decoration: none;
|
||
line-height: 1.4;
|
||
display: inline-block;
|
||
margin: 2px 5px 2px 0;
|
||
}
|
||
.the-tagcloud a:hover {
|
||
color: #135e96 !important;
|
||
text-decoration: underline;
|
||
}
|
||
/* Стили для обводки в выпадающем списке */
|
||
#tagsdiv-banned .select2-container--default .select2-search__field:focus {
|
||
outline: 1px solid <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
outline-offset: 0px !important;
|
||
}
|
||
|
||
#tagsdiv-keys .select2-container--default .select2-search__field:focus {
|
||
outline: 1px solid <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
outline-offset: 0px !important;
|
||
}
|
||
|
||
#tagsdiv-post_tag .select2-container--default .select2-search__field:focus,
|
||
.tagsdiv-post_tag .select2-container--default .select2-search__field:focus {
|
||
outline: 1px solid <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
outline-offset: 0px !important;
|
||
}
|
||
|
||
/* Дополнительно для браузеров которые используют box-shadow для фокуса */
|
||
#tagsdiv-banned .select2-container--default .select2-search__field:focus {
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['banned']['color']; ?> !important;
|
||
}
|
||
|
||
#tagsdiv-keys .select2-container--default .select2-search__field:focus {
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['keys']['color']; ?> !important;
|
||
}
|
||
|
||
#tagsdiv-post_tag .select2-container--default .select2-search__field:focus,
|
||
.tagsdiv-post_tag .select2-container--default .select2-search__field:focus {
|
||
box-shadow: 0 0 0 1px <?php echo $taxonomies_settings['post_tag']['color']; ?> !important;
|
||
}
|
||
|
||
.tagsdiv .howto {
|
||
display: none !important;
|
||
}
|
||
|
||
</style>
|
||
<?php
|
||
});
|
||
|
||
// AJAX обработчик для получения существующих терминов с сортировкой по релевантности
|
||
add_action('wp_ajax_get_existing_terms', function () {
|
||
$taxonomy = sanitize_key($_GET['taxonomy']);
|
||
$search = sanitize_text_field($_GET['search'] ?? '');
|
||
|
||
$args = [
|
||
'taxonomy' => $taxonomy,
|
||
'hide_empty' => false,
|
||
'number' => 100,
|
||
];
|
||
|
||
// Если есть поисковый запрос, добавляем поиск и сортировку по релевантности
|
||
if (!empty($search)) {
|
||
$args['search'] = $search;
|
||
$args['orderby'] = 'name';
|
||
$args['order'] = 'ASC';
|
||
}
|
||
|
||
$terms = get_terms($args);
|
||
|
||
$result = [];
|
||
foreach ($terms as $term) {
|
||
$result[] = [
|
||
'id' => $term->term_id,
|
||
'text' => $term->name
|
||
];
|
||
}
|
||
|
||
// Сортируем по релевантности, если есть поисковый запрос
|
||
if (!empty($search)) {
|
||
usort($result, function($a, $b) use ($search) {
|
||
$a_text = $a['text'];
|
||
$b_text = $b['text'];
|
||
$search_lower = strtolower($search);
|
||
|
||
// Приоритет: точное совпадение
|
||
if (strtolower($a_text) === $search_lower && strtolower($b_text) !== $search_lower) {
|
||
return -1;
|
||
}
|
||
if (strtolower($b_text) === $search_lower && strtolower($a_text) !== $search_lower) {
|
||
return 1;
|
||
}
|
||
|
||
// Приоритет: начинается с поискового запроса
|
||
$a_starts = stripos($a_text, $search) === 0;
|
||
$b_starts = stripos($b_text, $search) === 0;
|
||
|
||
if ($a_starts && !$b_starts) {
|
||
return -1;
|
||
}
|
||
if ($b_starts && !$a_starts) {
|
||
return 1;
|
||
}
|
||
|
||
// Приоритет: содержит поисковый запрос
|
||
$a_contains = stripos($a_text, $search) !== false;
|
||
$b_contains = stripos($b_text, $search) !== false;
|
||
|
||
if ($a_contains && !$b_contains) {
|
||
return -1;
|
||
}
|
||
if ($b_contains && !$a_contains) {
|
||
return 1;
|
||
}
|
||
|
||
// Если одинаковый приоритет - сортируем по алфавиту
|
||
return strcasecmp($a_text, $b_text);
|
||
});
|
||
}
|
||
|
||
wp_send_json($result);
|
||
});
|
||
|
||
// AJAX обработчик для получения терминов поста
|
||
add_action('wp_ajax_get_post_terms', function () {
|
||
$taxonomy = sanitize_key($_POST['taxonomy']);
|
||
$post_id = (int)$_POST['post_id'];
|
||
|
||
$terms = wp_get_post_terms($post_id, $taxonomy);
|
||
|
||
$result = [];
|
||
foreach ($terms as $term) {
|
||
$result[] = [
|
||
'id' => $term->term_id,
|
||
'text' => $term->name
|
||
];
|
||
}
|
||
|
||
wp_send_json($result);
|
||
});
|
||
|
||
// AJAX обработчик для получения популярных тегов
|
||
add_action('wp_ajax_get_popular_terms', function () {
|
||
$taxonomy = sanitize_key($_GET['taxonomy']);
|
||
|
||
// Получаем популярные теги (с наибольшим количеством постов)
|
||
$terms = get_terms([
|
||
'taxonomy' => $taxonomy,
|
||
'orderby' => 'count',
|
||
'order' => 'DESC',
|
||
'number' => 20,
|
||
'hide_empty' => false,
|
||
]);
|
||
|
||
$result = [];
|
||
foreach ($terms as $term) {
|
||
$result[] = [
|
||
'id' => $term->term_id,
|
||
'name' => $term->name,
|
||
'count' => $term->count // Добавляем количество использований
|
||
];
|
||
}
|
||
|
||
wp_send_json($result);
|
||
});
|
||
|
||
// Обработчик сохранения с исправленной логикой для новых тегов
|
||
add_action('save_post', function($post_id) {
|
||
// Проверяем права пользователя
|
||
if (!current_user_can('edit_post', $post_id)) return;
|
||
|
||
// Убираем автосохранение
|
||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||
|
||
// Отладочная информация
|
||
error_log('Save post hook called for post: ' . $post_id);
|
||
error_log('POST data for keys: ' . ($_POST['keys_ids'] ?? 'not set'));
|
||
error_log('POST data for banned: ' . ($_POST['banned_ids'] ?? 'not set'));
|
||
error_log('POST data for post_tag: ' . ($_POST['post_tag_ids'] ?? 'not set'));
|
||
|
||
$taxonomies = ['banned', 'keys', 'post_tag'];
|
||
|
||
foreach ($taxonomies as $taxonomy) {
|
||
if (isset($_POST[$taxonomy . '_ids'])) {
|
||
$values = explode(',', $_POST[$taxonomy . '_ids']);
|
||
$term_ids = [];
|
||
|
||
foreach ($values as $value) {
|
||
$value = trim($value);
|
||
if (empty($value)) continue;
|
||
|
||
// Если значение НЕ число, это новый тег (только для keys)
|
||
if (!is_numeric($value) && $taxonomy === 'keys') {
|
||
// Это название нового тега
|
||
$term_name = $value;
|
||
if (!empty($term_name)) {
|
||
// Создаем новый терм
|
||
$new_term = wp_insert_term($term_name, $taxonomy);
|
||
if (!is_wp_error($new_term)) {
|
||
$term_ids[] = $new_term['term_id'];
|
||
error_log('Created new term: ' . $term_name . ' with ID: ' . $new_term['term_id']);
|
||
} else if ($new_term->get_error_code() === 'term_exists') {
|
||
// Если терм уже существует, получаем его ID
|
||
$existing_term = get_term_by('name', $term_name, $taxonomy);
|
||
if ($existing_term) {
|
||
$term_ids[] = $existing_term->term_id;
|
||
error_log('Term already exists: ' . $term_name . ' with ID: ' . $existing_term->term_id);
|
||
}
|
||
} else {
|
||
error_log('Error creating term: ' . $term_name . ' - ' . $new_term->get_error_message());
|
||
}
|
||
}
|
||
} else {
|
||
// Существующий ID термина
|
||
$term_ids[] = intval($value);
|
||
}
|
||
}
|
||
|
||
$term_ids = array_filter($term_ids);
|
||
if (!empty($term_ids)) {
|
||
wp_set_object_terms($post_id, $term_ids, $taxonomy, false);
|
||
error_log('Set terms for ' . $taxonomy . ': ' . implode(', ', $term_ids));
|
||
} else {
|
||
// Если нет терминов, очищаем
|
||
wp_set_object_terms($post_id, [], $taxonomy, false);
|
||
error_log('Cleared terms for ' . $taxonomy);
|
||
}
|
||
}
|
||
}
|
||
});
|