+ запис

'Оберіть...', 'hide_empty' => 0, 'name' => 'st_category', 'id' => 'st_category', 'class' => 'st-select', 'orderby' => 'name', 'selected' => 0, )); ?>
'st_description', 'textarea_rows' => 15, 'media_buttons' => true, 'quicktags' => true, 'dfw' => true, // ПРИМУСОВО ВМИКАЄ ЗОСЕРЕДЖЕНИЙ РЕЖИМ (ДОПОМАГАЄ З ІКОНКАМИ) 'tinymce' => array( 'toolbar1' => 'formatselect,bold,italic,strikethrough,forecolor,|,bullist,numlist,blockquote,|,alignleft,aligncenter,alignright,alignjustify,|,link,unlink,|,removeformat,fullscreen', 'toolbar2' => '', 'toolbar3' => '', 'toolbar4' => '', 'fontsize_formats' => '10px,12px,14px,16px,18px,20px,24px,28px,32px', 'content_css' => false, 'content_style' => 'body { font-family: Georgia, "Times New Roman", Times, serif; font-size: 16px; line-height: 1.6; color: #1a1a1a; }', ), 'editor_height' => 300, 'editor_class' => 'st-editor-content', 'drag_drop_upload' => true, ); wp_editor('', 'st_description', $editor_settings); ?>
'Помилка безпеки. Оновіть сторінку.']); } $title = isset($_POST['st_title']) ? sanitize_text_field($_POST['st_title']) : ''; $content = isset($_POST['st_description']) ? wp_kses_post($_POST['st_description']) : ''; $cat_id = isset($_POST['st_category']) ? intval($_POST['st_category']) : 0; $password = isset($_POST['st_admin_pass']) ? $_POST['st_admin_pass'] : ''; $secret_key = '1234'; if ($password !== $secret_key) { wp_send_json_error(['message' => 'Невірний секретний код.']); } if (empty($title) || empty($content)) { wp_send_json_error(['message' => 'Заповніть заголовок та історію.']); } $admin = get_user_by('email', get_option('admin_email')); $author_id = $admin ? $admin->ID : 1; $post_data = array( 'post_title' => $title, 'post_content' => $content, 'post_status' => 'publish', 'post_author' => $author_id, 'post_type' => 'post', ); if ($cat_id > 0) { $post_data['post_category'] = array($cat_id); } $post_id = wp_insert_post($post_data, true); if (!is_wp_error($post_id) && $post_id > 0) { add_post_meta($post_id, '_frontend_posted', 'yes', true); $post_url = get_permalink($post_id); wp_send_json_success([ 'message' => 'Ура! Запис успішно опубліковано! 🎉', 'url' => $post_url ]); } else { $error_msg = is_wp_error($post_id) ? $post_id->get_error_message() : 'Невідома помилка'; wp_send_json_error(['message' => 'Помилка бази даних: ' . $error_msg]); } } /** * 1. Обробник AJAX-запиту для авторизації */ add_action('wp_ajax_nopriv_st_ajax_login', 'st_ajax_login_handler'); function st_ajax_login_handler() { // Перевірка безпеки (Nonce) check_ajax_referer('st-login-nonce', 'security'); $info = array(); $info['user_login'] = sanitize_user($_POST['log']); $info['user_password'] = $_POST['pwd']; $info['remember'] = true; // Запам'ятати користувача // Спроба авторизації через вбудовану функцію WordPress $user_signon = wp_signon($info, false); if (is_wp_error($user_signon)) { wp_send_json_error(array('message' => 'Помилка: Невірний логін або пароль.')); } else { wp_set_current_user($user_signon->ID); wp_send_json_success(array('message' => 'Успішно! Вхід виконано.')); } } /** * 2. Шорткод [st_login_btn] для виведення кнопки та модального вікна */ add_shortcode('st_login_btn', 'st_render_ajax_login_modal'); function st_render_ajax_login_modal() { // Якщо користувач вже авторизований, кнопку входу не показуємо if (is_user_logged_in()) { return ''; } // Підготовлюємо дані для JS $ajax_url = admin_url('admin-ajax.php'); $nonce = wp_create_nonce('st-login-nonce'); $html = '

Авторизація

'; return $html; } // add_action('admin_init', function() { remove_action('admin_init', 'send_frame_options_header', 10); }); /** * 2. ОСНОВНОЙ ШОРТКОД [st_edit_tree] */ add_shortcode('st_edit_tree', 'st_render_universal_editor_tree'); function st_render_universal_editor_tree() { // Проверка прав: только для авторизованных пользователей с правами редактирования if (!is_user_logged_in() || !current_user_can('edit_posts')) { return ''; } static $assets_loaded = false; $html = ''; if (!$assets_loaded) { $html .= st_get_editor_tree_assets(); $assets_loaded = true; } // Главная кнопка вызова интерфейса $html .= ''; // Модальное окно 1: Дерево выбора $html .= '

Выберите материал

'; // Сбор данных: Рубрики и их записи $cats = get_categories(['hide_empty' => true]); foreach ($cats as $cat) { $posts = get_posts(['category' => $cat->term_id, 'numberposts' => 100, 'post_status' => 'any']); if ($posts) { $html .= '
📂 ' . esc_html($cat->name) . ' (' . count($posts) . ')
'; foreach ($posts as $p) { // Принудительно задаем параметр Классического редактора $raw_link = get_edit_post_link($p->ID, 'raw'); $classic_link = add_query_arg('classic-editor', '1', $raw_link); $html .= '📄 ' . esc_html($p->post_title) . ''; } $html .= '
'; } } // Сбор данных: Страницы $pages = get_pages(['number' => 100]); if ($pages) { $html .= '
📂 Все страницы
'; foreach ($pages as $pg) { // Принудительно задаем параметр Классического редактора $raw_link = get_edit_post_link($pg->ID, 'raw'); $classic_link = add_query_arg('classic-editor', '1', $raw_link); $html .= '📑 ' . esc_html($pg->post_title) . ''; } $html .= '
'; } $html .= '
'; // Модальное окно 2: Встроенный редактор (Iframe) $html .= '

Редактор (Классический)

Загрузка интерфейса...
'; return $html; } /** * 3. СТИЛИ И СКРИПТЫ */ function st_get_editor_tree_assets() { return ' '; } // // Добавляем шорткод [st_vine_buds] 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(); $assets_loaded = true; } $html .= '
'; $html .= '
'; // Центральный ствол лозы // Получаем рубрики $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 .= '
'; $html .= '
'; $html .= '
'; $html .= '

' . esc_html($cat->name) . '

'; // Контейнер для почек и иконки $html .= '
'; // Скрытый список для модального окна $hidden_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 .= ''; $html .= ' '; $html .= ''; // Добавление элемента в скрытый список $hidden_list .= '
  • ' . $post_title . '
  • '; } $hidden_list .= '
'; // Иконка в конце ветки (кнопка списка) $html .= '
'; $html .= ' '; $html .= '
'; // Сохраняем скрытый HTML списка сразу после кнопки для доступа через JS $html .= ' '; $html .= '
'; // Конец st-buds-row $html .= '
'; // Конец st-branch-content $html .= '
'; // Конец контейнера ветки } } $html .= '
'; // Конец st-vine-wrapper // Глобальное модальное окно (одно на всю страницу) $html .= '

Рубрика

'; return $html; } // CSS и JS function st_get_vine_buds_assets() { return ' '; } //кабинет // ========================================================= // ОСОБИСТИЙ КАБІНЕТ DOCTOR DOBRA (Шорткод: [st_cabinet]) // ========================================================= /** * 1. ШОРТКОД ЛИЧНОГО КАБИНЕТА */ add_shortcode('st_cabinet', 'st_render_personal_cabinet'); function st_render_personal_cabinet() { static $assets_loaded = false; $html = ''; if (!$assets_loaded) { $html .= st_get_cabinet_assets(); $assets_loaded = true; } // Если гость — показываем форму Входа / Регистрации if (!is_user_logged_in()) { return $html . st_get_auth_forms_html(); } // Если авторизован — показываем Личный кабинет $current_user = wp_get_current_user(); $logout_url = wp_logout_url(get_permalink()); $html .= '
'; $html .= '
'; $html .= '

Особистий кабінет

'; $html .= ' Вийти '; $html .= '
'; // Навигация (Табы) $html .= '
'; $html .= ' '; $html .= ' '; $html .= ' '; $html .= ' '; $html .= ' '; $html .= '
'; $html .= '
'; // ТАБ 1: ПРОФИЛЬ $html .= '
'; $html .= '
'; $html .= '
' . get_avatar($current_user->ID, 80) . '
'; $html .= ' '; $html .= '
'; $html .= '
'; // ТАБ 2: АНАМНЕЗ (НОВЫЙ) $html .= '
'; $html .= '

Медичний Анамнез

'; $html .= '
'; $html .= '

Анкета здоров\'я

'; $html .= '

Будь ласка, заповніть цю анкету перед першою консультацією. Це допоможе лікарю краще підготуватися до вашого візиту та підібрати ефективний план дій.

'; // Сюда можно будет вставить ссылку на Google Форму или форму на сайте $html .= ' Заповнити анкету'; $html .= '
'; $html .= '
'; // ТАБ 3: ПРОЧИТАННОЕ $html .= '
'; $html .= '

Історія читання

'; $read_posts = get_user_meta($current_user->ID, '_st_read_posts', true); if (!empty($read_posts) && is_array($read_posts)) { $html .= '
    '; // Показываем последние 10 прочитанных $recent_posts = array_slice(array_reverse($read_posts), 0, 10); foreach ($recent_posts as $post_id) { if (get_post_status($post_id) === 'publish') { $html .= '
  • 📌 ' . get_the_title($post_id) . '
  • '; } } $html .= '
'; } else { $html .= '
Ви ще не дочитали жодної статті до кінця. Перейдіть у блог!
'; $html .= ' Читати блог'; } $html .= '
'; // ТАБ 4: КОНСУЛЬТАЦИИ $html .= '
'; $html .= '

Ваші консультації

'; $html .= '
'; $html .= '

Запис на прийом

'; $html .= '

Щоб записатися на індивідуальну консультацію або супровід, перейдіть до календаря.

'; $html .= ' Обрати час'; $html .= '
'; $html .= '
'; // ТАБ 5: ПОДПИСКА (ГАЙДЫ И МЕНЮ) $html .= '
'; $html .= '

Підписка

'; $html .= '
'; $html .= '
'; $html .= '
🥑
'; $html .= '

Меню для ШКТ

'; $html .= ' Завантажити PDF'; $html .= '
'; $html .= '
'; $html .= '
📚
'; $html .= '

Закритий клуб

'; $html .= ' Оформити'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= '
'; // Конец контента $html .= '
'; // Конец враппера return $html; } /** * 2. ФОРМЫ АВТОРИЗАЦИИ И РЕГИСТРАЦИИ (HTML) */ function st_get_auth_forms_html() { $ajax_url = admin_url('admin-ajax.php'); $nonce = wp_create_nonce('st-auth-nonce'); return '
'; } /** * 3. ОБРАБОТЧИКИ AJAX: Вход и Регистрация */ add_action('wp_ajax_nopriv_st_cab_ajax_login', 'st_cab_ajax_login_handler'); function st_cab_ajax_login_handler() { check_ajax_referer('st-auth-nonce', 'security'); $user = wp_signon(['user_login' => $_POST['log'], 'user_password' => $_POST['pwd'], 'remember' => true], false); if (is_wp_error($user)) { wp_send_json_error(['message' => 'Невірний логін або пароль.']); } else { wp_set_current_user($user->ID); wp_send_json_success(['message' => 'Успішно! Завантаження кабінету...']); } } add_action('wp_ajax_nopriv_st_cab_ajax_register', 'st_cab_ajax_register_handler'); function st_cab_ajax_register_handler() { check_ajax_referer('st-auth-nonce', 'security'); $email = sanitize_email($_POST['email']); $name = sanitize_text_field($_POST['name']); $password = $_POST['pwd']; if (email_exists($email) || username_exists($email)) { wp_send_json_error(['message' => 'Користувач з таким Email вже існує.']); } $user_id = wp_create_user($email, $password, $email); if (is_wp_error($user_id)) { wp_send_json_error(['message' => $user_id->get_error_message()]); } else { wp_update_user(['ID' => $user_id, 'display_name' => $name, 'first_name' => $name]); wp_signon(['user_login' => $email, 'user_password' => $password, 'remember' => true], false); wp_send_json_success(['message' => 'Реєстрація успішна! Входимо...']); } } /** * 4. ОТСЛЕЖИВАНИЕ ПРОЧИТАННЫХ СТАТЕЙ (AJAX) */ add_action('wp_footer', 'st_track_read_articles_script'); function st_track_read_articles_script() { if (is_single() && is_user_logged_in()) { $post_id = get_the_ID(); $nonce = wp_create_nonce('st-track-nonce'); ?> :root { --ink-black: #1a1a1a; --marker-yellow: #ffde59; --marker-green: #c1ff00; --marker-blue: #38b6ff; --bg-paper: #fdfcf8; --shadow-comic: 4px 4px 0px var(--ink-black); --border-sketch: 2px solid var(--ink-black); } .st-cab-wrapper { max-width: 800px; margin: 40px auto; background: var(--bg-paper); border: var(--border-sketch); border-radius: 20px; box-shadow: 8px 8px 0px rgba(0,0,0,0.1); overflow: hidden; font-family: Georgia, serif; } .st-cab-header { display: flex; justify-content: space-between; align-items: center; padding: 20px; border-bottom: var(--border-sketch); background: #fff; } .st-cab-title { margin: 0; font-size: 24px; font-weight: 800; color: var(--ink-black); } .st-cab-logout-btn { display: flex; align-items: center; gap: 8px; color: #ef4444; font-weight: bold; text-decoration: none; font-family: sans-serif; font-size: 14px; } .st-cab-nav { display: flex; overflow-x: auto; border-bottom: var(--border-sketch); background: #f0e9da; } .st-cab-nav::-webkit-scrollbar { display: none; } .st-cab-tab { flex: 1; padding: 15px 10px; border: none; background: none; cursor: pointer; font-weight: bold; font-family: sans-serif; font-size: 14px; color: #555; border-right: var(--border-sketch); transition: 0.2s; white-space: nowrap; } .st-cab-tab:last-child { border-right: none; } .st-cab-tab:hover { background: rgba(0,0,0,0.05); } .st-cab-tab.active { background: #fff; color: var(--ink-black); border-bottom: 3px solid var(--ink-black); } .st-cab-content { padding: 30px 20px; min-height: 300px; background: #fff; } .st-cab-pane { display: none; animation: fadeIn 0.4s; } .st-cab-pane.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .st-cab-pane h3 { margin-top: 0; margin-bottom: 20px; font-weight: 800; } .st-profile-card { display: flex; gap: 20px; align-items: center; } .st-avatar img { border-radius: 50%; border: var(--border-sketch); box-shadow: var(--shadow-comic); } .st-user-info h3 { margin: 0 0 5px 0; font-size: 22px; } .st-user-info p { margin: 0 0 10px 0; color: #666; font-family: sans-serif; } .st-badge { background: var(--marker-yellow); padding: 4px 10px; border-radius: 20px; border: var(--border-sketch); font-size: 12px; font-weight: bold; font-family: sans-serif; } .st-btn-action { display: inline-flex; align-items: center; justify-content: center; padding: 10px 20px; border-radius: 30px; border: var(--border-sketch); box-shadow: var(--shadow-comic); color: var(--ink-black); text-decoration: none; font-weight: bold; font-family: sans-serif; transition: 0.2s; cursor: pointer; background: #fff; } .st-btn-action:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0px var(--ink-black); } .st-btn-action:active { transform: translate(2px, 2px); box-shadow: 0px 0px 0px var(--ink-black); } .st-info-card { padding: 20px; border: var(--border-sketch); border-radius: 16px; box-shadow: var(--shadow-comic); background: #fffdf5; } .st-guides-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; } .st-guide-card { border: var(--border-sketch); border-radius: 16px; padding: 20px; text-align: center; box-shadow: var(--shadow-comic); background: #fff; display: flex; flex-direction: column; align-items: center; gap: 10px; } .st-guide-icon { font-size: 40px; } .st-guide-card h4 { margin: 0; font-family: sans-serif; font-size: 16px; } .st-locked { opacity: 0.7; background: #f0f0f0; } .st-read-list { list-style: none; padding: 0; margin: 0; } .st-read-list li { margin-bottom: 10px; } .st-read-list a { display: block; padding: 12px 16px; border: var(--border-sketch); border-radius: 12px; text-decoration: none; color: var(--ink-black); font-family: sans-serif; font-weight: bold; transition: 0.2s; background: #fff; } .st-read-list a:hover { background: var(--marker-green); transform: translateX(5px); } .st-empty-state { padding: 30px; text-align: center; border: 2px dashed #ccc; border-radius: 16px; color: #666; font-family: sans-serif; } .st-auth-wrapper { max-width: 400px; margin: 50px auto; font-family: sans-serif; } .st-auth-box { background: #fff; border: var(--border-sketch); border-radius: 20px; padding: 25px; box-shadow: 8px 8px 0px rgba(0,0,0,0.1); } .st-auth-tabs { display: flex; margin-bottom: 20px; border-bottom: 2px solid #eee; } .st-auth-tab { flex: 1; padding: 10px; border: none; background: none; font-size: 16px; font-weight: bold; cursor: pointer; color: #888; transition: 0.2s; } .st-auth-tab.active { color: var(--ink-black); border-bottom: 3px solid var(--ink-black); } .st-input { width: 100%; padding: 12px 15px; margin-bottom: 15px; border: var(--border-sketch); border-radius: 12px; box-sizing: border-box; font-size: 15px; font-family: inherit; } .st-input:focus { outline: none; border-color: #38b6ff; box-shadow: 0 0 0 3px rgba(56, 182, 255, 0.2); } .st-auth-msg { margin-top: 15px; padding: 10px; border-radius: 8px; font-size: 14px; text-align: center; display: none; } .st-auth-msg.success { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; } .st-auth-msg.error { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; } @media (max-width: 600px) { .st-cab-header { flex-direction: column; gap: 10px; align-items: flex-start; } } '; } /** * Літо 2026 · Меню-комікс * REST API та таблиці БД для збереження даних користувачів */ // Створення таблиць при активації register_activation_hook(__FILE__, 'summer_menu_create_tables'); function summer_menu_create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_users = $wpdb->prefix . 'summer_users'; $table_checklists = $wpdb->prefix . 'summer_checklists'; $table_completed = $wpdb->prefix . 'summer_completed_days'; $table_notes = $wpdb->prefix . 'summer_notes'; $sql_users = "CREATE TABLE $table_users ( id mediumint(9) NOT NULL AUTO_INCREMENT, phone varchar(20) NOT NULL UNIQUE, name varchar(100) DEFAULT '', token varchar(64) DEFAULT '', created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY phone (phone) ) $charset_collate;"; $sql_checklists = "CREATE TABLE $table_checklists ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id mediumint(9) NOT NULL, week_key varchar(20) NOT NULL, item_id varchar(255) NOT NULL, checked tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY user_item (user_id, week_key, item_id) ) $charset_collate;"; $sql_completed = "CREATE TABLE $table_completed ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id mediumint(9) NOT NULL, week_idx varchar(10) NOT NULL, day_idx int NOT NULL, PRIMARY KEY (id), UNIQUE KEY user_day (user_id, week_idx, day_idx) ) $charset_collate;"; $sql_notes = "CREATE TABLE $table_notes ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id mediumint(9) NOT NULL, day_key varchar(255) NOT NULL, note_text text NOT NULL, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY user_day_key (user_id, day_key) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql_users); dbDelta($sql_checklists); dbDelta($sql_completed); dbDelta($sql_notes); } // REST API маршрути add_action('rest_api_init', 'summer_menu_rest_routes'); function summer_menu_rest_routes() { register_rest_route('summer/v1', '/auth', array( 'methods' => 'POST', 'callback' => 'summer_auth_callback', 'permission_callback' => '__return_true' )); register_rest_route('summer/v1', '/data', array( 'methods' => 'POST', 'callback' => 'summer_sync_data_callback', 'permission_callback' => 'summer_verify_token' )); register_rest_route('summer/v1', '/data', array( 'methods' => 'GET', 'callback' => 'summer_get_data_callback', 'permission_callback' => 'summer_verify_token' )); register_rest_route('summer/v1', '/profile', array( 'methods' => 'POST', 'callback' => 'summer_update_profile_callback', 'permission_callback' => 'summer_verify_token' )); } // Авторизація function summer_auth_callback($request) { $phone = sanitize_text_field($request->get_param('phone')); $code = sanitize_text_field($request->get_param('code')); if ($code !== '2026') { return new WP_Error('wrong_code', 'Невірний код', array('status' => 403)); } if (empty($phone)) { return new WP_Error('no_phone', 'Телефон обов’язковий', array('status' => 400)); } global $wpdb; $table_users = $wpdb->prefix . 'summer_users'; $user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_users WHERE phone = %s", $phone)); if (!$user) { $token = bin2hex(random_bytes(32)); $wpdb->insert($table_users, array( 'phone' => $phone, 'token' => $token, 'created_at' => current_time('mysql') )); $user_id = $wpdb->insert_id; $name = ''; } else { $user_id = $user->id; $token = $user->token; $name = $user->name; if (empty($token)) { $token = bin2hex(random_bytes(32)); $wpdb->update($table_users, array('token' => $token), array('id' => $user_id)); } } return rest_ensure_response(array( 'success' => true, 'user_id' => $user_id, 'token' => $token, 'name' => $name )); } function summer_verify_token($request) { $token = $request->get_header('X-Auth-Token'); if (!$token) return false; global $wpdb; $table_users = $wpdb->prefix . 'summer_users'; $user = $wpdb->get_row($wpdb->prepare("SELECT id FROM $table_users WHERE token = %s", $token)); if (!$user) return false; $request->set_param('user_id', $user->id); return true; } function summer_get_data_callback($request) { $user_id = $request->get_param('user_id'); global $wpdb; $table_checklists = $wpdb->prefix . 'summer_checklists'; $table_completed = $wpdb->prefix . 'summer_completed_days'; $table_notes = $wpdb->prefix . 'summer_notes'; $checklists = $wpdb->get_results($wpdb->prepare( "SELECT week_key, item_id, checked FROM $table_checklists WHERE user_id = %d", $user_id )); $completed = $wpdb->get_results($wpdb->prepare( "SELECT week_idx, day_idx FROM $table_completed WHERE user_id = %d", $user_id )); $notes = $wpdb->get_results($wpdb->prepare( "SELECT day_key, note_text FROM $table_notes WHERE user_id = %d", $user_id )); $checklist_state = array(); foreach ($checklists as $item) { $checklist_state[$item->week_key][$item->item_id] = (bool)$item->checked; } $completed_state = array(); foreach ($completed as $day) { $completed_state[$day->week_idx . '_' . $day->day_idx] = true; } $notes_state = array(); foreach ($notes as $note) { $notes_state[$note->day_key] = $note->note_text; } return rest_ensure_response(array( 'checklists' => $checklist_state, 'completed' => $completed_state, 'notes' => $notes_state )); } function summer_sync_data_callback($request) { $user_id = $request->get_param('user_id'); $data = $request->get_json_params(); global $wpdb; $table_checklists = $wpdb->prefix . 'summer_checklists'; $table_completed = $wpdb->prefix . 'summer_completed_days'; $table_notes = $wpdb->prefix . 'summer_notes'; if (isset($data['checklists'])) { foreach ($data['checklists'] as $week_key => $items) { foreach ($items as $item_id => $checked) { $wpdb->replace($table_checklists, array( 'user_id' => $user_id, 'week_key' => $week_key, 'item_id' => $item_id, 'checked' => $checked ? 1 : 0 )); } } } if (isset($data['completed'])) { $wpdb->delete($table_completed, array('user_id' => $user_id)); foreach ($data['completed'] as $key => $value) { if (!$value) continue; list($week_idx, $day_idx) = explode('_', $key); $wpdb->insert($table_completed, array( 'user_id' => $user_id, 'week_idx' => $week_idx, 'day_idx' => intval($day_idx) )); } } if (isset($data['notes'])) { foreach ($data['notes'] as $day_key => $note_text) { if (empty($note_text)) { $wpdb->delete($table_notes, array('user_id' => $user_id, 'day_key' => $day_key)); } else { $wpdb->replace($table_notes, array( 'user_id' => $user_id, 'day_key' => $day_key, 'note_text' => $note_text )); } } } return rest_ensure_response(array('success' => true)); } function summer_update_profile_callback($request) { $user_id = $request->get_param('user_id'); $name = sanitize_text_field($request->get_param('name')); global $wpdb; $table_users = $wpdb->prefix . 'summer_users'; $wpdb->update($table_users, array('name' => $name), array('id' => $user_id)); return rest_ensure_response(array('success' => true, 'name' => $name)); }