[ 'note' => '', 'reminder' => '' ], ... ] */ public static function get_bulk( array $user_ids ): array { global $wpdb; if ( empty( $user_ids ) ) { return []; } $ids_sql = implode( ',', array_map( 'intval', $user_ids ) ); $keys = [ self::META_NOTE, self::META_REMINDER ]; $map = []; foreach ( $user_ids as $id ) { $map[ (int) $id ] = [ 'note' => '', 'reminder' => '' ]; } foreach ( $keys as $meta_key ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $rows = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ({$ids_sql})", $meta_key ) ); foreach ( $rows as $row ) { $uid = (int) $row->user_id; if ( $meta_key === self::META_NOTE ) { $map[ $uid ]['note'] = (string) $row->meta_value; } else { $map[ $uid ]['reminder'] = (string) $row->meta_value; } } } return $map; } // ------------------------------------------------------------------------- // AJAX — save // ------------------------------------------------------------------------- public static function ajax_save_note() { check_ajax_referer( self::NONCE_ACTION, 'nonce' ); if ( ! current_user_can( 'edit_users' ) ) { wp_send_json_error( [ 'message' => __( 'Permission denied.', 'aisp-customer-activity' ) ], 403 ); } $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; $note = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : ''; $reminder = isset( $_POST['reminder'] ) ? sanitize_text_field( wp_unslash( $_POST['reminder'] ) ) : ''; if ( ! $user_id || ! get_userdata( $user_id ) ) { wp_send_json_error( [ 'message' => __( 'Invalid user.', 'aisp-customer-activity' ) ], 400 ); } // Validate date format if provided. if ( $reminder && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $reminder ) ) { $reminder = ''; } update_user_meta( $user_id, self::META_NOTE, $note ); update_user_meta( $user_id, self::META_REMINDER, $reminder ); wp_send_json_success( [ 'note' => $note, 'reminder' => $reminder, 'reminder_formatted' => $reminder ? date_i18n( get_option( 'date_format' ), strtotime( $reminder ) ) : '', ] ); } // ------------------------------------------------------------------------- // AJAX — get (for populating the form on open) // ------------------------------------------------------------------------- public static function ajax_get_note() { check_ajax_referer( self::NONCE_ACTION, 'nonce' ); if ( ! current_user_can( 'edit_users' ) ) { wp_send_json_error( [], 403 ); } $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; if ( ! $user_id ) { wp_send_json_error( [], 400 ); } wp_send_json_success( [ 'note' => self::get_note( $user_id ), 'reminder' => self::get_reminder( $user_id ), ] ); } // ------------------------------------------------------------------------- // Render helpers (called from dormant render_row) // ------------------------------------------------------------------------- /** * Render the note cell content for a customer row. * * @param int $user_id * @param string $note Pre-fetched note text. * @param string $reminder Pre-fetched reminder date (Y-m-d). */ public static function render_note_cell( int $user_id, string $note, string $reminder ) { $has_note = $note !== ''; $has_reminder = $reminder !== ''; $reminder_fmt = $has_reminder ? date_i18n( get_option( 'date_format' ), strtotime( $reminder ) ) : ''; // Is the reminder overdue? $is_overdue = $has_reminder && strtotime( $reminder ) < strtotime( 'today' ); $reminder_color = $is_overdue ? '#c0392b' : '#2271b1'; echo ''; // Summary line shown when form is closed. echo '
'; if ( $has_reminder ) { echo '📅 ' . esc_html( $reminder_fmt ) . ''; } if ( $has_note ) { echo '' . esc_html( wp_trim_words( $note, 6, '…' ) ) . ''; } // Toggle button. $btn_label = $has_note || $has_reminder ? __( '✏️ Edit', 'aisp-customer-activity' ) : __( '📝 Add note', 'aisp-customer-activity' ); echo ''; echo '
'; // .aisp-ca-note-summary // Inline form (hidden by default). echo ''; // .aisp-ca-note-form echo ''; } // ------------------------------------------------------------------------- // Enqueue JS + pass vars // ------------------------------------------------------------------------- public static function enqueue_scripts() { wp_enqueue_script( 'aisp-ca-notes', AISP_CA_PLUGIN_URL . 'assets/js/aisp-ca-notes.js', [ 'jquery' ], AISP_CA_VERSION, true ); wp_localize_script( 'aisp-ca-notes', 'aispCaNotes', [ 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( self::NONCE_ACTION ), 'i18n' => [ 'saving' => __( 'Saving…', 'aisp-customer-activity' ), 'saved' => __( 'Saved!', 'aisp-customer-activity' ), 'error' => __( 'Error.', 'aisp-customer-activity' ), 'addNote' => __( '📝 Add note', 'aisp-customer-activity' ), 'edit' => __( '✏️ Edit', 'aisp-customer-activity' ), ], ] ); } }