Files
profile/inc/admin/lock-terms.php
Profile Profile ed4d79b706 add files inc
2026-03-09 20:51:08 +03:00

807 lines
35 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);
}
}
}
});