get_charset_collate(); require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $connection_table = $wpdb->prefix . 'stw_connection'; $sql_connection = "CREATE TABLE $connection_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, jwt_token text NOT NULL, shop_id varchar(255) DEFAULT NULL, connected_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; dbDelta( $sql_connection ); // Create page-to-item mapping table (supports both items and rooms). $page_item_table = $wpdb->prefix . 'stw_linked_objects'; $sql_page_items = "CREATE TABLE $page_item_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, page_id bigint(10) NOT NULL, entity_id varchar(255) NOT NULL, entity_type varchar(20) DEFAULT 'item', created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY page_id (page_id) ) $charset_collate;"; dbDelta( $sql_page_items ); } /** * Save JWT token and shop ID to database. * * @param string $jwt_token JWT token from STW platform. * @param string $shop_id Shop ID from STW platform. * @return int|false Insert/update ID or false on failure. */ public static function save_jwt( $jwt_token, $shop_id ) { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_connection'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe (prefix + hardcoded). $existing = $wpdb->get_row( "SELECT id FROM $table_name LIMIT 1" ); $data = array( 'jwt_token' => $jwt_token, 'updated_at' => current_time( 'mysql' ), 'shop_id' => $shop_id, ); if ( $existing ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table update. $wpdb->update( $table_name, $data, array( 'id' => $existing->id ) ); return $existing->id; } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom plugin table insert. $wpdb->insert( $table_name, $data ); return $wpdb->insert_id; } } /** * Get stored JWT token. * * @return string|null JWT token or null if not found. */ public static function get_jwt() { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_connection'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe. return $wpdb->get_var( "SELECT jwt_token FROM $table_name LIMIT 1" ); } /** * Get stored shop ID. * * @return string|null Shop ID or null if not found. */ public static function get_shop_id() { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_connection'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe. return $wpdb->get_var( "SELECT shop_id FROM $table_name LIMIT 1" ); } /** * Link a page to an STW item or room. * * @param int $page_id WordPress page/post ID. * @param string $entity_id STW item or room ID. * @param string $entity_type Type: 'item' or 'room' (default 'item'). * @return int|false Insert/update ID or false on failure. */ public static function link_page_to_item( $page_id, $entity_id, $entity_type = 'item' ) { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_linked_objects'; // Check if link already exists. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe. $existing = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM $table_name WHERE page_id = %d", $page_id ) ); $data = array( 'page_id' => $page_id, 'entity_id' => $entity_id, 'entity_type' => $entity_type, 'updated_at' => current_time( 'mysql' ), ); if ( $existing ) { // Update existing link. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table update. $wpdb->update( $table_name, $data, array( 'id' => $existing->id ) ); return $existing->id; } else { // Insert new link. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom plugin table insert. $wpdb->insert( $table_name, $data ); return $wpdb->insert_id; } } /** * Get the item ID linked to a page. * * @param int $page_id WordPress page/post ID. * @return string|null Item ID or null if not linked. */ public static function get_page_item_id( $page_id ) { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_linked_objects'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe. return $wpdb->get_var( $wpdb->prepare( "SELECT entity_id FROM $table_name WHERE page_id = %d", $page_id ) ); } /** * Get complete link data for a page (ID and type). * * @param int $page_id WordPress page/post ID. * @return array|null Array with entity_id and entity_type, or null if not linked. */ public static function get_page_link_data( $page_id ) { global $wpdb; // Table name safely constructed from trusted $wpdb->prefix + hardcoded string. $table_name = $wpdb->prefix . 'stw_linked_objects'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom plugin table. Table name is safe. $row = $wpdb->get_row( $wpdb->prepare( "SELECT entity_id, entity_type FROM $table_name WHERE page_id = %d", $page_id ), ARRAY_A ); return $row; } /** * Unlink a page from its STW item. * * @param int $page_id WordPress page/post ID. * @return bool True on success, false on failure. */ public static function unlink_page_from_item( $page_id ) { global $wpdb; $table_name = $wpdb->prefix . 'stw_linked_objects'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table delete. return $wpdb->delete( $table_name, array( 'page_id' => $page_id ) ) !== false; } /** * Drop the database tables when plugin is uninstalled. */ public static function drop_table() { global $wpdb; // Table names safely constructed from trusted $wpdb->prefix + hardcoded string. $connection_table = $wpdb->prefix . 'stw_connection'; $page_items_table = $wpdb->prefix . 'stw_linked_objects'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Intentional table drop. Table names are safe. $wpdb->query( "DROP TABLE IF EXISTS $connection_table" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Intentional table drop. Table names are safe. $wpdb->query( "DROP TABLE IF EXISTS $page_items_table" ); } }