Перейти к основному содержимомуПерейти к верхней панели
- О WordPress
- https://i.spirittrade.kz/
- Ctrl+KОткрыть палитру команд
- 00 комментариев ждут одобрения
- Добавить
- Привет, admin
Помощь
Редактировать темы
Редактирование Twenty Twenty-Five (активен)
Файл: functions.phpВыберите тему для изменения: Twenty Twenty-Five
Файлы темы
-
- style.css
- functions.php
- assetsпапка
- style.min.css
- partsпапка
- templatesпапка
- package-lock.json
- package.json
- stylesпапка
- theme.json
- functions(08062026).php
- functions(1).php
- patternsпапка
- contributing.txt
- readme.txt
Выбранное содержимое файла:
1
<?php
2
/*
3
Plugin Name: SpiritTrade Digital Modules
4
Description: Единый плагин для формы постов, карты базы знаний, древа категорий, Summer Menu API и AL-Тиндер.
5
Version: 3.1.1
6
Author: Spirit Trade
7
*/
8
9
// ==============================================================================
10
// 0. ГЛОБАЛЬНЫЕ КОНСТАНТЫ И НАСТРОЙКИ
11
// ==============================================================================
12
define( 'ST_TG_BOT_TOKEN', '7675966976:AAFmAeowI9eGA5siDvsUy9kPr_SHZIOBI38' );
13
define( 'ST_TG_CHAT_ID', '-1002640050542' );
14
define( 'ST_ADMIN_PIN', '16' );
15
16
// ==============================================================================
17
// 1. ИНИЦИАЛИЗАЦИЯ БАЗЫ ДАННЫХ И ПОДКЛЮЧЕНИЕ СКРИПТОВ
18
// ==============================================================================
19
20
add_action( 'wp_enqueue_scripts', 'st_enqueue_global_assets' );
get_charset_collate(); $prefix = $wpdb->prefix; require_once( ABSPATH . ‘wp-admin/includes/upgrade.php’ ); $tables = [ «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_profile ( user_id BIGINT UNSIGNED NOT NULL, nickname VARCHAR(100) DEFAULT », age INT DEFAULT NULL, engagement_level VARCHAR(50) DEFAULT », bio TEXT, platform_goal VARCHAR(100) DEFAULT », languages LONGTEXT, current_vibe_id INT DEFAULT NULL, vibe_updated_at DATETIME DEFAULT NULL, PRIMARY KEY (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_locations ( user_id BIGINT UNSIGNED NOT NULL, last_coordinates POINT DEFAULT NULL, habitat_zones LONGTEXT, saved_venues LONGTEXT, PRIMARY KEY (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_beverage_preferences ( user_id BIGINT UNSIGNED NOT NULL, spirits LONGTEXT, wine LONGTEXT, beer_cider LONGTEXT, fermented LONGTEXT, PRIMARY KEY (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_lifestyle ( user_id BIGINT UNSIGNED NOT NULL, favorite_time VARCHAR(50) DEFAULT », occupation VARCHAR(100) DEFAULT », event_format VARCHAR(50) DEFAULT », outing_frequency VARCHAR(50) DEFAULT », venue_choice VARCHAR(50) DEFAULT », company_pref VARCHAR(50) DEFAULT », venue_atmosphere VARCHAR(50) DEFAULT », comm_style VARCHAR(50) DEFAULT », rating_style VARCHAR(50) DEFAULT », explorer_type VARCHAR(50) DEFAULT », PRIMARY KEY (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_media_prompts ( user_id BIGINT UNSIGNED NOT NULL, photos LONGTEXT, interest_tags LONGTEXT, text_prompts LONGTEXT, spotify_anthem VARCHAR(255) DEFAULT », social_links LONGTEXT, PRIMARY KEY (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_vibes_dictionary ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, category VARCHAR(50) NOT NULL, code VARCHAR(10) NOT NULL, name VARCHAR(100) NOT NULL, emoji VARCHAR(10) NOT NULL, description TEXT, PRIMARY KEY (id), UNIQUE KEY code (code) ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_user_vibe_history ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL, vibe_id INT UNSIGNED NOT NULL, set_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY user_id (user_id), FOREIGN KEY (user_id) REFERENCES {$prefix}users(ID) ON DELETE CASCADE, FOREIGN KEY (vibe_id) REFERENCES {$prefix}st_al_vibes_dictionary(id) ON DELETE RESTRICT ) $charset_collate;», «CREATE TABLE IF NOT EXISTS {$prefix}st_al_vibe_recommendation_rules ( vibe_id INT UNSIGNED NOT NULL, target_beverage_category LONGTEXT, target_venue_atmosphere LONGTEXT, alternative_options LONGTEXT, PRIMARY KEY (vibe_id), FOREIGN KEY (vibe_id) REFERENCES {$prefix}st_al_vibes_dictionary(id) ON DELETE CASCADE ) $charset_collate;», // Кастомная таблица для данных Summer Menu (изолирует wp_options) «CREATE TABLE IF NOT EXISTS {$prefix}st_sm_user_data ( phone VARCHAR(50) NOT NULL, checks LONGTEXT, completed LONGTEXT, notes LONGTEXT, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (phone) ) $charset_collate;» ]; foreach ($tables as $sql) { dbDelta( $sql ); } if ( ! $wpdb->get_var( «SELECT COUNT(*) FROM {$prefix}st_al_vibes_dictionary» ) ) { st_al_seed_vibes( «{$prefix}st_al_vibes_dictionary» ); } update_option( ‘st_global_tables_installed_v3’, 1 ); } // ============================================================================== // 2. ФОРМА ПУБЛИКАЦИИ:
Доступ закрыт
Для публикации новых заметок в базу знаний платформы необходимо авторизоваться.
+ запись
Заголовок
0)); foreach($categories as $category) { echo » . esc_html($category->name) . ‘
‘; } ?>
‘st_description’, ‘textarea_rows’ => 8, ‘media_buttons’ => true, ‘quicktags’ => true, ‘dfw’ => true, ‘tinymce’ => array( ‘toolbar1’ => ‘bold,italic,link,wp_adv’, ‘toolbar2’ => ‘formatselect,strikethrough,forecolor,bullist,numlist,blockquote,alignleft,aligncenter,alignright,unlink,removeformat,fullscreen’, ‘toolbar3’ => », ‘toolbar4’ => », ‘fontsize_formats’ => ’10px,12px,14px,16px,18px,20px,24px’, ‘content_css’ => false, ‘content_style’ => ‘body { font-family: Georgia, «Times New Roman», Times, serif; font-size: 16px; line-height: 1.5; color: #1a1a1a; }’, ), ‘editor_height’ => 150, ‘editor_class’ => ‘st-editor-content’, ‘drag_drop_upload’ => true, ); wp_editor(», ‘st_description’, $editor_settings); ?>
ПИН-КОД
— — 134679 Опубликовать! ‘Ошибка безопасности. Обновите страницу.’ ) ); } $pin = isset( $_POST[‘st_admin_pass’] ) ? sanitize_text_field( $_POST[‘st_admin_pass’] ) : »; if ( $pin !== ST_ADMIN_PIN ) { wp_send_json_error( array( ‘message’ => ‘Неверный ПИН-код.’ ) ); } $title = sanitize_text_field( $_POST[‘st_title’] ); $content = wp_kses_post( $_POST[‘st_description’] ); $category_id = intval( $_POST[‘st_category’] ); if ( empty( $title ) ) { wp_send_json_error( array( ‘message’ => ‘Заполните заголовок.’ ) ); } if ( $category_id === 0 ) { wp_send_json_error( array( ‘message’ => ‘Выберите категорию.’ ) ); } $post_data = array( ‘post_title’ => $title, ‘post_content’ => $content, ‘post_status’ => ‘publish’, ‘post_type’ => ‘post’, ‘post_category’ => array( $category_id ), ); $post_id = wp_insert_post( $post_data, true ); if ( is_wp_error( $post_id ) ) { wp_send_json_error( array( ‘message’ => ‘Ошибка создания записи: ‘ . $post_id->get_error_message() ) ); } else { st_send_telegram_notification( $post_id, $title, $category_id ); wp_send_json_success( array( ‘message’ => ‘✅ Запись опубликована! ID: ‘ . $post_id ) ); } } function st_send_telegram_notification( $post_id, $title, $category_id ) { $category_name = ‘Без категории’; $cat = get_term( $category_id, ‘category’ ); if ( $cat && ! is_wp_error( $cat ) ) { $category_name = $cat->name; } $post_url = get_permalink( $post_id ); $message = «📢 *Spirit Trade Distribution | Новая запись*\n\n»; $message .= «📌 *{$title}*\n»; $message .= «🗂 Категория: {$category_name}\n»; $message .= «🔗 [Читать запись]({$post_url})\n»; $message .= «🕒 » . current_time( ‘d.m.Y H:i’ ); $url = «https://api.telegram.org/bot» . ST_TG_BOT_TOKEN . «/sendMessage»; $args = array( ‘body’ => array( ‘chat_id’ => ST_TG_CHAT_ID, ‘text’ => $message, ‘parse_mode’ => ‘Markdown’, ‘disable_web_page_preview’ => false, ), ‘timeout’ => 5, ); $response = wp_remote_post( $url, $args ); if ( is_wp_error( $response ) ) { error_log( ‘Telegram notification error: ‘ . $response->get_error_message() ); } } // ============================================================================== // 3. БАЗА ЗНАНИЙ:
‘ . $icon . »; $btn_inner .= ‘
‘; $btn_inner .= » . $main_title . »; if ($sub_title) { $btn_inner .= ‘для: ‘ . $sub_title . ‘‘; } $btn_inner .= »; $sidebar_html .= sprintf( ‘%s’, esc_attr($cat->term_id), esc_attr($cat->count), esc_attr($number), esc_attr($main_title), esc_attr($sub_title), esc_attr($desc), esc_attr($raw_name), $btn_inner ); } ?>
<style>
.st-kb-container {
--bg-paper: #fdfcf8;
--ink-black: #1a1a1a;
--marker-yellow: #ffde59;
--marker-green: #c1ff00;
--marker-blue: #38b6ff;
--border-sketch: 2px solid var(--ink-black);
--transition: 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
--font-main: Georgia, 'Times New Roman', Times, serif;
}
.st-kb-container .st-workspace {
display: flex; height: calc(100vh - 60px); min-height: 500px;
width: 100%; margin: 20px 0; background: var(--bg-paper);
font-family: var(--font-main); color: var(--ink-black);
overflow: hidden; box-sizing: border-box;
background-image: radial-gradient(var(--ink-black) 1px, transparent 1px);
background-size: 20px 20px; border: var(--border-sketch);
border-radius: 16px; box-shadow: 6px 6px 0 var(--ink-black);
position: relative;
}
.st-kb-container .st-sidebar {
width: 280px; background: #fff; border-right: var(--border-sketch);
display: flex; flex-direction: column;
transition: width var(--transition), transform var(--transition);
flex-shrink: 0; z-index: 20;
}
.st-kb-container .st-sidebar.collapsed { width: 75px; }
.st-kb-container .st-sidebar-header {
padding: 15px; display: flex; align-items: center; gap: 12px;
border-bottom: 2px dashed rgba(0,0,0,0.15); background: #fff;
flex-wrap: wrap;
}
.st-kb-container .st-toggle-btn {
background: #f5f5f5; border: 1px solid var(--ink-black); font-size: 1.2rem; cursor: pointer;
color: var(--ink-black); transition: var(--transition); box-shadow: 2px 2px 0 var(--ink-black);
width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
border-radius: 8px; flex-shrink: 0;
}
.st-kb-container .st-toggle-btn:active { transform: translateY(2px); box-shadow: 0 0 0 var(--ink-black); }
.st-kb-container .st-sidebar.collapsed .st-toggle-btn { transform: rotate(180deg); }
.st-kb-container .st-sidebar-title-group {
display: flex; align-items: center; gap: 10px;
flex: 1; min-width: 0;
transition: opacity 0.2s;
}
.st-kb-container .st-sidebar.collapsed .st-sidebar-title-group {
opacity: 0; pointer-events: none; display: none;
}
.st-kb-container .st-sidebar-title-text {
font-weight: 900; font-size: 0.9rem; line-height: 1.2;
text-transform: uppercase; letter-spacing: 0.5px;
background: #f0f0e8; padding: 6px 10px; border-radius: 8px;
border-left: 3px solid var(--ink-black);
white-space: normal; word-break: break-word;
}
.st-kb-container .st-guide-icon {
width: 22px; height: 22px; font-size: 0.7rem;
background: #fff; border: 2px solid var(--ink-black);
box-shadow: 2px 2px 0 var(--ink-black);
}
.st-kb-container .st-guide-icon:hover {
background: var(--marker-yellow); transform: translateY(-2px) scale(1.1);
box-shadow: 3px 3px 0 var(--ink-black);
}
.st-kb-container .st-cat-btn {
display: flex; align-items: center; width: 100%; padding: 10px 15px;
border: none; background: transparent; cursor: pointer;
text-align: left; border-bottom: 1px dashed rgba(0,0,0,0.05);
transition: background 0.2s; box-sizing: border-box;
position: relative;
}
.st-kb-container .st-cat-btn:hover { background: #fdfcf8; }
.st-kb-container .st-cat-btn.active { background: #f4f9f4; box-shadow: inset 4px 0 0 var(--ink-black); }
.st-kb-container .st-cat-icon {
width: 40px; height: 40px; min-width: 40px;
border: 2px solid var(--ink-black); border-radius: 10px 4px 10px 4px;
display: flex; justify-content: center; align-items: center;
font-family: var(--font-main); font-weight: 900; font-size: 1.5rem; color: var(--ink-black);
box-shadow: 2px 2px 0 var(--ink-black); transition: var(--transition);
}
.st-kb-container .st-cat-btn.active .st-cat-icon { transform: scale(1.05) rotate(-3deg); border-width: 3px; }
@media (hover: hover) { .st-kb-container .st-cat-btn:hover .st-cat-icon { transform: translateY(-2px) rotate(2deg); } }
.st-kb-container .st-cat-text {
display: flex; flex-direction: column; margin-left: 15px;
white-space: nowrap; opacity: 1; transition: opacity 0.2s; overflow: hidden;
flex: 1;
}
.st-kb-container .st-sidebar.collapsed .st-cat-text { opacity: 0; display: none; }
.st-kb-container .st-cat-main { font-weight: 900; font-size: 0.95rem; letter-spacing: 0.5px; text-overflow: ellipsis; overflow: hidden; }
.st-kb-container .st-cat-sphere {
font-size: 0.7rem; margin-top: 2px; color: var(--ink-black);
display: flex; align-items: baseline; gap: 4px;
}
.st-kb-container .st-cat-sphere small { color: #777; font-size: 0.6rem; text-transform: uppercase; font-weight: bold; }
.st-kb-container .st-cat-sphere strong {
font-weight: 900; letter-spacing: 0.5px;
background: linear-gradient(to top, rgba(255,222,89, 0.6) 40%, transparent 40%);
padding: 0 2px;
}
.st-kb-container .st-main-content {
flex: 1; display: flex; flex-direction: column; position: relative;
background: rgba(253, 252, 248, 0.9); backdrop-filter: blur(2px);
overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--ink-black) transparent;
}
.st-kb-container .st-main-content::-webkit-scrollbar { width: 6px; }
.st-kb-container .st-main-content::-webkit-scrollbar-thumb { background: var(--ink-black); border-radius: 10px; }
.st-kb-container .header-group {
padding: 20px 30px; position: sticky; top: 0;
background: rgba(253, 252, 248, 0.95); backdrop-filter: blur(5px);
z-index: 10; border-bottom: 2px dashed rgba(0,0,0,0.15);
display: flex; flex-direction: column; gap: 12px;
}
.st-kb-container .st-header-title-row {
display: flex; align-items: center; gap: 12px;
flex-wrap: nowrap; width: 100%; overflow: hidden;
}
.st-kb-container .cat-seq-badge {
background: var(--marker-yellow); border: 2.5px solid var(--ink-black);
width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
font-family: var(--font-main); font-size: 1.4rem; font-weight: 900;
border-radius: 8px 4px 10px 4px; box-shadow: 3px 3px 0 var(--ink-black);
transform: rotate(-3deg); flex-shrink: 0;
}
.st-kb-container .category-header {
font-weight: 900; font-size: clamp(1.1rem, 2.5vw, 1.6rem); text-transform: uppercase;
margin: 0; line-height: 1.1; color: var(--ink-black); letter-spacing: 0.5px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
flex-shrink: 1;
}
.st-kb-container .cat-info-btn {
background: #fff; border: 1.5px solid var(--ink-black); border-radius: 50%;
width: 27px; height: 27px; padding: 0; font-family: var(--font-main);
font-weight: bold; font-size: 0.84rem; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
box-shadow: 2px 2px 0 var(--ink-black); transition: var(--transition);
color: var(--ink-black); line-height: 1;
}
.st-kb-container .cat-info-btn:hover {
background: var(--marker-yellow); transform: translateY(-2px); box-shadow: 3px 3px 0 var(--ink-black);
}
.st-kb-container .cat-info-btn:active {
transform: translateY(1px); box-shadow: 1px 1px 0 var(--ink-black);
}
.st-kb-container .st-meta-sentence {
display: flex; align-items: baseline; gap: 8px;
flex-wrap: nowrap;
background: rgba(255, 255, 255, 0.6); border: 1px dashed rgba(0,0,0,0.2);
padding: 8px 12px; border-radius: 8px; width: fit-content;
max-width: 100%; overflow-x: auto;
scrollbar-width: none; white-space: nowrap;
}
.st-kb-container .st-meta-sentence::-webkit-scrollbar { display: none; }
.st-kb-container .category-stats-number {
font-size: 1rem; font-weight: 900; background: var(--marker-green);
display: inline-flex; justify-content: center; align-items: center;
padding: 2px 8px; min-width: 22px; transform: rotate(-2deg);
border: 2px solid var(--ink-black); box-shadow: 2px 2px 0 var(--ink-black);
border-radius: 6px; flex-shrink: 0; position: relative; top: 2px;
}
.st-kb-container .category-stats-label {
font-size: 0.85rem; font-weight: bold; color: #333;
text-transform: uppercase; letter-spacing: 0.5px; flex-shrink: 0;
}
.st-kb-container .st-header-sphere {
display: inline-flex; align-items: baseline; gap: 6px; flex-shrink: 0;
}
.st-kb-container .sphere-value {
font-size: 0.95rem; font-weight: 900; color: var(--ink-black);
text-transform: uppercase; letter-spacing: 0.5px;
background: linear-gradient(to top, rgba(255,222,89, 0.7) 40%, transparent 40%);
padding: 0 4px;
}
.st-kb-container .timeline { padding: 30px 40px 60px 40px; position: relative; width: 100%; box-sizing: border-box; max-width: 900px; }
.st-kb-container .timeline::before { content: ''; position: absolute; left: 40px; top: 30px; bottom: 0; width: 3px; background: var(--ink-black); }
.st-kb-container .timeline-item {
position: relative; margin-bottom: 30px; padding-left: 30px;
text-decoration: none; display: flex; flex-direction: column;
align-items: flex-start; gap: 8px; opacity: 0; transform: translateX(-15px);
transition: var(--transition); width: 100%; box-sizing: border-box;
}
.st-kb-container .timeline-item.show { opacity: 1; transform: translateX(0); }
.st-kb-container .date-box {
background: var(--marker-blue); border: 2px solid var(--ink-black); padding: 3px 10px;
font-weight: bold; font-size: 0.75rem; color: var(--ink-black);
transform: rotate(-1deg); box-shadow: 2px 2px 0px var(--ink-black); z-index: 2;
}
.st-kb-container .leaf-box {
background: #fff; border: var(--border-sketch); padding: 15px 20px;
border-radius: 0px 15px 4px 15px; box-shadow: 3px 3px 0px var(--ink-black);
transition: var(--transition); max-width: 100%; width: 100%; box-sizing: border-box; z-index: 1;
}
@media (hover: hover) { .st-kb-container .timeline-item:hover .leaf-box { transform: scale(1.01) translateX(5px); background: #fdfdfd; } }
.st-kb-container .timeline-title {
font-weight: bold; font-size: clamp(1rem, 2vw, 1.2rem); margin: 0;
line-height: 1.4; color: var(--ink-black); word-break: break-word;
}
.st-kb-container .timeline-excerpt {
font-size: 0.9rem; line-height: 1.5; color: #444; margin: 10px 0 0 0; padding-top: 10px;
border-top: 1px dashed rgba(26,26,26,0.2); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
overflow: hidden; text-overflow: ellipsis;
}
.st-kb-container .timeline-item::after { content: '📌'; position: absolute; left: -10px; top: 25px; font-size: 20px; filter: drop-shadow(1px 1px 0px white); }
.st-kb-container #st-kb-loader { text-align: center; font-weight: bold; font-size: 1.1rem; padding: 40px; }
.st-kb-container .st-mobile-overlay, .st-kb-container .st-desc-modal-overlay {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5); backdrop-filter: blur(2px);
z-index: 15; opacity: 0; visibility: hidden; transition: 0.3s;
}
.st-kb-container .st-mobile-overlay.show { opacity: 1; visibility: visible; }
.st-kb-container .st-desc-modal-overlay { z-index: 100; display: flex; align-items: center; justify-content: center; }
.st-kb-container .st-desc-modal-overlay.active { opacity: 1; visibility: visible; }
.st-kb-container .st-desc-modal-box {
background: var(--bg-paper); width: 90%; max-width: 450px;
border: var(--border-sketch); border-radius: 12px; box-shadow: 6px 6px 0 var(--ink-black);
transform: translateY(20px) rotate(-1deg); transition: 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
background-image: radial-gradient(var(--ink-black) 1px, transparent 1px); background-size: 15px 15px;
max-height: 80vh; display: flex; flex-direction: column;
}
.st-kb-container .st-desc-modal-overlay.active .st-desc-modal-box { transform: translateY(0) rotate(0); }
.st-kb-container .st-desc-header {
padding: 15px 20px; border-bottom: var(--border-sketch); background: var(--marker-yellow);
border-radius: 10px 10px 0 0; display: flex; justify-content: space-between; align-items: center;
}
.st-kb-container .st-desc-header h3 { margin: 0; font-size: 1.1rem; font-weight: 900; text-transform: uppercase; }
.st-kb-container .st-desc-close {
background: #ff4444; border: 2px solid var(--ink-black); border-radius: 50%;
width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 1.2rem; cursor: pointer; box-shadow: 2px 2px 0 var(--ink-black);
transition: 0.2s; padding: 0; line-height: 1;
}
.st-kb-container .st-desc-close:hover { transform: scale(1.1) rotate(10deg); background: #ff3333; }
.st-kb-container .st-desc-body {
padding: 20px; font-size: 0.95rem; line-height: 1.6; font-weight: 500;
background: rgba(255,255,255,0.9); border-radius: 0 0 10px 10px; overflow-y: auto;
}
@media (max-width: 800px) {
.st-kb-container .st-workspace { border-radius: 12px; margin: 10px 0; height: calc(100vh - 20px); min-height: 550px; box-shadow: 4px 4px 0 var(--ink-black); }
.st-kb-container .st-sidebar { position: absolute; height: 100%; z-index: 20; transform: translateX(-100%); width: 280px; box-shadow: 10px 0 25px rgba(0,0,0,0.2); }
.st-kb-container .st-sidebar.mobile-open { transform: translateX(0); }
.st-kb-container .st-sidebar-header { justify-content: space-between; }
.st-kb-container .st-toggle-btn { transform: rotate(180deg); }
.st-kb-container .mobile-menu-trigger {
display: flex; align-items: center; justify-content: space-between;
width: 100%; margin-bottom: 15px; background: #fff;
border: 2px solid var(--ink-black); padding: 8px 12px;
border-radius: 12px; box-shadow: 4px 4px 0 var(--ink-black);
cursor: pointer; transition: 0.1s; color: var(--ink-black);
}
.st-kb-container .mobile-menu-trigger:active { transform: translateY(3px); box-shadow: 1px 1px 0 var(--ink-black); }
.st-kb-container .trigger-left { display: flex; align-items: center; gap: 12px; }
.st-kb-container .trigger-icon {
display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; background: var(--marker-yellow);
border: 2px solid var(--ink-black); border-radius: 8px; font-size: 1.4rem; box-shadow: 2px 2px 0 var(--ink-black);
}
.st-kb-container .trigger-text-group { display: flex; flex-direction: column; text-align: left; }
.st-kb-container .trigger-text { font-weight: 900; font-size: 1.05rem; text-transform: uppercase; letter-spacing: 0.5px; line-height: 1.1; }
.st-kb-container .trigger-sub { font-size: 0.7rem; font-weight: bold; color: #666; margin-top: 2px; }
.st-kb-container .trigger-arrow {
display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: #f4f4f4; border: 2px solid var(--ink-black); border-radius: 50%;
}
.st-kb-container .timeline { padding: 15px 15px 40px 20px; }
.st-kb-container .timeline::before { left: 15px; top: 15px; }
.st-kb-container .timeline-item::after { left: -10px; top: 22px; font-size: 16px; }
.st-kb-container .timeline-item { padding-left: 20px; }
.st-kb-container .header-group { padding: 15px; }
.st-kb-container .leaf-box { padding: 12px 15px; }
.st-kb-container .st-header-title-row { gap: 8px; }
.st-kb-container .cat-seq-badge { width: 32px; height: 32px; font-size: 1.1rem; }
.st-kb-container .st-meta-sentence { padding: 6px 10px; border: none; background: transparent; }
.st-kb-container .sphere-value { font-size: 0.85rem; }
.st-kb-container .st-guide-icon { width: 16px; height: 16px; font-size: 0.6rem; }
.st-kb-container .cat-info-btn { width: 24px; height: 24px; font-size: 0.7rem; }
}
@media (min-width: 801px) {
.st-kb-container .mobile-menu-trigger { display: none; }
}
</style>
<div class="st-kb-container" id="st-kb-wrap">
<div class="st-workspace">
<div class="st-mobile-overlay" id="st-mobile-overlay"></div>
<div class="st-desc-modal-overlay" id="st-desc-modal">
<div class="st-desc-modal-box">
<div class="st-desc-header">
<h3 id="st-desc-modal-title">Описание</h3>
<button class="st-desc-close" id="st-desc-close">×</button>
</div>
<div class="st-desc-body" id="st-desc-body"></div>
</div>
</div>
<aside class="st-sidebar" id="st-sidebar">
<div class="st-sidebar-header">
<button id="st-toggle-btn" class="st-toggle-btn">≡</button>
<div class="st-sidebar-title-group">
<div class="st-sidebar-title-text">НАЖМИ на</div>
<span class="st-info-trigger st-guide-icon" data-info-title="Как это работает?" data-info-content="📌 Это окно появляется при нажатии на значок ❔.<br>Оно выводит описание о том объекте, рядом с которым он расположен.<br><br><strong>Пример:</strong> Так как этот значок расположен здесь, он выводит информацию о данном разделе:<br><div style='margin-top:10px; padding:10px; background:rgba(255,222,89,0.15); border-left:3px solid var(--ink-black); font-style:normal;'><strong>Разделы упорядочены по номерам</strong> и содержат информацию, необходимую для реализации проекта в указанной сфере деятельности.<br><br>Нажмите на любой раздел — справа отобразятся все публикации, относящиеся к этой теме.</div>">❔</span>
</div>
</div>
<div class="st-sidebar-menu">
<?php echo $sidebar_html; ?>
</div>
</aside>
<main class="st-main-content" id="st-main-content">
<div class="header-group">
<button class="mobile-menu-trigger" id="st-mobile-trigger">
<div class="trigger-left">
<span class="trigger-icon">🗂️</span>
<div class="trigger-text-group">
<span class="trigger-text">Направления</span>
<span class="trigger-sub">Показать все разделы</span>
</div>
</div>
<div class="trigger-arrow">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
</div>
</button>
<div class="st-header-title-row">
<div class="cat-seq-badge" id="cat-seq-display" style="display:none;"></div>
<h1 class="category-header" id="current-category-title">Загрузка...</h1>
<button class="cat-info-btn" id="cat-info-btn" style="display: none;" title="Описание рубрики">📖</button>
</div>
<div class="st-meta-sentence">
<span class="category-stats-number" id="cat-count-display">0</span>
<span class="category-stats-label" id="cat-stats-label">записей по теме</span>
<span class="st-header-sphere" id="cat-sphere-display" style="display:none;">
<span class="sphere-value" id="cat-sphere-text"></span>
</span>
</div>
</div>
<div id="st-kb-loader">🔄 Синхронизация данных...</div>
<div class="timeline" id="timeline-container"></div>
</main>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const wrap = document.getElementById('st-kb-wrap');
const sidebar = wrap.querySelector('#st-sidebar');
const toggleBtn = wrap.querySelector('#st-toggle-btn');
const mobileTrigger = wrap.querySelector('#st-mobile-trigger');
const overlay = wrap.querySelector('#st-mobile-overlay');
const tabs = wrap.querySelectorAll('.st-cat-btn');
const container = wrap.querySelector('#timeline-container');
const titleEl = wrap.querySelector('#current-category-title');
const seqDisplay = wrap.querySelector('#cat-seq-display');
const countEl = wrap.querySelector('#cat-count-display');
const statsLabel = wrap.querySelector('#cat-stats-label');
const sphereDisplay = wrap.querySelector('#cat-sphere-display');
const sphereText = wrap.querySelector('#cat-sphere-text');
const infoBtn = wrap.querySelector('#cat-info-btn');
const descModal = wrap.querySelector('#st-desc-modal');
const descModalTitle = wrap.querySelector('#st-desc-modal-title');
const descBody = wrap.querySelector('#st-desc-body');
const closeDesc = wrap.querySelector('#st-desc-close');
const loader = wrap.querySelector('#st-kb-loader');
const contentArea = wrap.querySelector('#st-main-content');
const apiUrl = '<?php echo esc_url( rest_url( 'wp/v2' ) ); ?>';
function openInfoModal(title, contentHtml) {
descModalTitle.innerHTML = title;
descBody.innerHTML = contentHtml;
descModal.classList.add('active');
}
closeDesc.addEventListener('click', () => descModal.classList.remove('active'));
descModal.addEventListener('click', (e) => { if(e.target === descModal) descModal.classList.remove('active'); });
const infoTriggers = wrap.querySelectorAll('.st-info-trigger');
infoTriggers.forEach(trigger => {
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const title = trigger.getAttribute('data-info-title') || 'Информация';
const content = trigger.getAttribute('data-info-content') || '<em>Нет данных</em>';
openInfoModal(title, content);
});
});
function closeMobileMenu() {
sidebar.classList.remove('mobile-open');
overlay.classList.remove('show');
}
function openMobileMenu() {
sidebar.classList.add('mobile-open');
overlay.classList.add('show');
}
toggleBtn.addEventListener('click', () => {
if (window.innerWidth > 800) sidebar.classList.toggle('collapsed');
else closeMobileMenu();
});
mobileTrigger.addEventListener('click', openMobileMenu);
overlay.addEventListener('click', closeMobileMenu);
infoBtn.addEventListener('click', () => {
const activeTab = wrap.querySelector('.st-cat-btn.active');
if (activeTab) {
const desc = activeTab.getAttribute('data-desc');
const mainTitle = activeTab.getAttribute('data-main');
if (desc && desc.trim() !== '') {
openInfoModal('О рубрике: ' + mainTitle, desc);
} else {
openInfoModal('О рубрике', '<em>Описание отсутствует</em>');
}
}
});
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const catId = this.getAttribute('data-id');
const catCount = this.getAttribute('data-count') || 0;
const catNum = this.getAttribute('data-number');
const mainTitle = this.getAttribute('data-main');
const subTitle = this.getAttribute('data-sub');
const catDesc = this.getAttribute('data-desc');
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
if (window.innerWidth <= 800) closeMobileMenu();
container.innerHTML = '';
if (catNum && catNum.trim() !== '') {
seqDisplay.innerText = catNum;
seqDisplay.style.display = 'flex';
} else {
seqDisplay.style.display = 'none';
}
titleEl.innerText = mainTitle;
countEl.innerText = catCount;
if (subTitle && subTitle.trim() !== '') {
statsLabel.innerText = 'записей для:';
sphereText.innerText = subTitle;
sphereDisplay.style.display = 'inline-flex';
} else {
statsLabel.innerText = 'записей по теме';
sphereDisplay.style.display = 'none';
}
if (catDesc && catDesc.trim() !== '') {
infoBtn.style.display = 'inline-flex';
} else {
infoBtn.style.display = 'none';
}
if (!catId) {
loader.style.display = 'none';
container.innerHTML = `<div class="date-box" style="margin:20px auto; background:#ff4444; color:#fff;">Ошибка: ID не найден.</div>`;
return;
}
fetchPosts(catId);
});
});
if(tabs.length > 0) tabs[0].click();
else { loader.style.display = 'none'; titleEl.innerText = 'Нет рубрик'; }
function fetchPosts(categoryId) {
loader.style.display = 'block';
loader.innerText = '📌 Подгружаем данные...';
contentArea.scrollTo({top: 0, behavior: 'smooth'});
fetch(`${apiUrl}/posts?categories=${categoryId}&per_page=50`)
.then(response => response.json())
.then(posts => {
loader.style.display = 'none';
if (!Array.isArray(posts) || posts.length === 0) {
container.innerHTML = '<div class="date-box" style="margin:20px;">Нет записей в данной категории</div>';
return;
}
posts.forEach((post, index) => {
const dateObj = new Date(post.date);
const date = dateObj.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' });
let excerptText = '';
if (post.excerpt && post.excerpt.rendered) {
let tempDiv = document.createElement('div');
tempDiv.innerHTML = post.excerpt.rendered;
excerptText = tempDiv.textContent || tempDiv.innerText || '';
excerptText = excerptText.trim();
}
let excerptHtml = excerptText ? `<div class="timeline-excerpt">${excerptText}</div>` : '';
const item = document.createElement('a');
item.href = post.link;
item.className = 'timeline-item';
item.innerHTML = `
<div class="date-box">${date}</div>
<div class="leaf-box">
<h3 class="timeline-title">${post.title.rendered}</h3>
${excerptHtml}
</div>
`;
container.appendChild(item);
setTimeout(() => { item.classList.add('show'); }, 50 * index);
});
})
.catch(err => {
loader.style.display = 'none';
container.innerHTML = '<div class="date-box" style="margin:20px; background:#ff4444; color:#fff;">Ошибка получения данных</div>';
});
}
});
</script>
<?php
return ob_get_clean();
}
// ==============================================================================
// 4. ШОРТКОД ДРЕВА/ЛОЗЫ КАТЕГОРИЙ:
// ==============================================================================
add_shortcode(‘st_vine_buds’, ‘st_render_vine_buds_layout’);
function st_render_vine_buds_layout() {
static $buds_assets_loaded = false;
$html = »;
if (!$buds_assets_loaded) {
$html .= st_get_vine_buds_assets();
$buds_assets_loaded = true;
}
$html .= '<div class="st-vine-container" id="st-vine-comp">';
$html .= '<div class="st-vine-wrapper">';
$html .= '<div class="st-vine-trunk"></div>';
$categories = get_categories(array('hide_empty' => true));
$branch_side = 'left';
foreach ($categories as $index => $cat) {
$posts = get_posts(array(
'category' => $cat->term_id,
'numberposts' => 20,
'post_status' => 'publish'
));
if ($posts) {
$branch_side = ($index % 2 === 0) ? 'left' : 'right';
$html .= '<div class="st-vine-branch-container st-side-' . $branch_side . '">';
$html .= ' <div class="st-branch-line"></div>';
$html .= ' <div class="st-branch-content">';
$html .= ' <h3 class="st-branch-title">' . esc_html($cat->name) . '</h3>';
$html .= ' <div class="st-buds-row">';
$hidden_list = '<ul class="st-modal-post-list">';
foreach ($posts as $post_index => $p) {
$post_url = esc_url(get_permalink($p->ID));
$post_title = esc_html($p->post_title);
$post_date = get_the_date('d.m.Y', $p->ID);
$is_top = ($post_index % 2 === 0);
$position_class = $is_top ? 'st-bud-top' : 'st-bud-bottom';
$html .= '<a href="' . $post_url . '" class="st-bud-wrapper ' . $position_class . '" data-tooltip="' . $post_title . '">';
$html .= ' <span class="st-bud-shape"></span>';
$html .= '</a>';
$hidden_list .= '<li><span class="st-post-date">' . $post_date . '</span> <a href="' . $post_url . '">' . $post_title . '</a></li>';
}
$hidden_list .= '</ul>';
$html .= ' <div class="st-branch-end-icon" data-cat="' . esc_attr($cat->name) . '" data-tooltip="Все записи">';
$html .= ' <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>';
$html .= ' </div>';
$html .= ' <div class="st-hidden-data" style="display: none;">' . $hidden_list . '</div>';
$html .= ' </div>';
$html .= ' </div>';
$html .= '</div>';
}
}
$html .= '</div>';
$html .= '
<div id="st-vine-modal" class="st-vine-overlay">
<div class="st-vine-modal-box">
<div class="st-vine-modal-header">
<h3 id="st-vine-modal-title">Рубрика</h3>
<button class="st-vine-close" id="st-vine-close-btn">×</button>
</div>
<div class="st-vine-modal-body" id="st-vine-modal-body">
</div>
</div>
</div>';
$html .= '</div>'; // Конец st-vine-container
return $html;
}
function st_get_vine_buds_assets() {
return ‘
function st_enqueue_global_assets() {
22
if ( ! is_admin() ) {
23
wp_enqueue_style( 'dashicons' );
24
wp_enqueue_style( 'editor-buttons' );
25
wp_enqueue_style( 'tinymce-css', includes_url( 'css/editor.min.css' ) );
26
wp_enqueue_script( 'jquery' );
27
28
// Подключаем тяжелые скрипты редактора только если они нужны
29
if ( shortcode_exists( 'frontend_post_form' ) || shortcode_exists( 'al_profile_setup' ) ) {
30
wp_enqueue_editor();
31
wp_enqueue_media();
32
wp_enqueue_script( 'word-count' );
33
wp_enqueue_script( 'editor' );
34
wp_enqueue_script( 'quicktags' );
35
}
36
}
37
}
38
39
add_action( 'plugins_loaded', 'st_global_install_tables' );
Документация:Название функции…WP_REST_Response()abs()add_action()add_filter()add_options_page()add_shortcode()admin_url()array_fill()array_map()array_merge()array_slice()array_unshift()check_admin_referer()check_ajax_referer()count()crc32()current_time()dbDelta()define()error_log()esc_attr()esc_html()esc_textarea()esc_url()esc_url_raw()explode()file_exists()finfo_close()finfo_file()finfo_open()get_bloginfo()get_categories()get_current_user_id()get_option()get_permalink()get_posts()get_term()get_the_date()get_userdata()home_url()implode()in_array()includes_url()intval()is_admin()is_array()is_user_logged_in()is_wp_error()json_decode()mb_substr()md5()move_uploaded_file()mt_rand()ob_get_clean()ob_start()pathinfo()preg_match()register_rest_route()rest_ensure_response()rest_url()sanitize_email()sanitize_text_field()sanitize_textarea_field()shortcode_exists()sprintf()stripslashes()strtoupper()substr()time()trim()uniqid()update_option()wp_create_nonce()wp_dequeue_style()wp_editor()wp_enqueue_editor()wp_enqueue_media()wp_enqueue_script()wp_enqueue_style()wp_get_current_user()wp_get_image_editor()wp_insert_post()wp_json_encode()wp_kses_post()wp_login_url()wp_logout_url()wp_mkdir_p()wp_nonce_field()wp_registration_url()wp_remote_post()wp_send_json_error()wp_send_json_success()wp_trim_words()wp_upload_dir()wp_verify_nonce()
Спасибо вам за творчество с WordPress.
Версия 7.0
Добавить комментарий