PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Admin / RemoteInboxNotifications / RemoteInboxNotificationsEngine.php
woocommerce / src / Admin / RemoteInboxNotifications Last commit date
RemoteInboxNotificationsDataSourcePoller.php 3 months ago RemoteInboxNotificationsEngine.php 2 weeks ago RuleProcessorInterface.php 1 year ago SpecRunner.php 2 months ago TransformerInterface.php 1 year ago
RemoteInboxNotificationsEngine.php
337 lines
1 <?php
2 /**
3 * Handles running specs
4 */
5
6 namespace Automattic\WooCommerce\Admin\RemoteInboxNotifications;
7
8 defined( 'ABSPATH' ) || exit;
9
10 use Automattic\WooCommerce\Admin\Notes\Notes;
11 use Automattic\WooCommerce\Admin\PluginsProvider\PluginsProvider;
12 use Automattic\WooCommerce\Internal\Admin\Onboarding\OnboardingProfile;
13 use Automattic\WooCommerce\Admin\Notes\Note;
14 use Automattic\WooCommerce\Admin\RemoteSpecs\RemoteSpecsEngine;
15 use Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\StoredStateSetupForProducts;
16
17 /**
18 * Remote Inbox Notifications engine.
19 * This goes through the specs and runs (creates admin notes) for those
20 * specs that are able to be triggered.
21 */
22 class RemoteInboxNotificationsEngine extends RemoteSpecsEngine {
23 const STORED_STATE_OPTION_NAME = 'wc_remote_inbox_notifications_stored_state';
24 const WCA_UPDATED_OPTION_NAME = 'wc_remote_inbox_notifications_wca_updated';
25
26 /**
27 * Initialize the engine.
28 * phpcs:disable WooCommerce.Functions.InternalInjectionMethod.MissingFinal
29 * phpcs:disable WooCommerce.Functions.InternalInjectionMethod.MissingInternalTag
30 */
31 public static function init() {
32 // Init things that need to happen before admin_init.
33 add_action( 'init', array( __CLASS__, 'on_init' ), 0, 0 );
34
35 // Continue init via admin_init.
36 add_action( 'admin_init', array( __CLASS__, 'on_admin_init' ) );
37
38 // Trigger when the profile data option is updated (during onboarding).
39 add_action(
40 'update_option_' . OnboardingProfile::DATA_OPTION,
41 array( __CLASS__, 'update_profile_option' ),
42 10,
43 2
44 );
45
46 // Hook into WCA updated. This is hooked up here rather than in
47 // on_admin_init because that runs too late to hook into the action.
48 add_action(
49 'woocommerce_run_on_woocommerce_admin_updated',
50 array( __CLASS__, 'run_on_woocommerce_admin_updated' )
51 );
52 add_action(
53 'woocommerce_updated',
54 function () {
55 $next_hook = WC()->queue()->get_next(
56 'woocommerce_run_on_woocommerce_admin_updated',
57 array(),
58 'woocommerce-remote-inbox-engine'
59 );
60 if ( null === $next_hook ) {
61 WC()->queue()->schedule_single(
62 time(),
63 'woocommerce_run_on_woocommerce_admin_updated',
64 array(),
65 'woocommerce-remote-inbox-engine'
66 );
67 }
68 }
69 );
70
71 add_filter( 'woocommerce_get_note_from_db', array( __CLASS__, 'get_note_from_db' ), 10, 1 );
72 add_filter( 'woocommerce_debug_tools', array( __CLASS__, 'add_debug_tools' ) );
73 add_action(
74 'wp_ajax_woocommerce_json_inbox_notifications_search',
75 array( __CLASS__, 'ajax_action_inbox_notification_search' )
76 );
77 }
78
79 /**
80 * This is triggered when the profile option is updated and if the
81 * profiler is being completed, triggers a run of the engine.
82 *
83 * @param mixed $old_value Old value.
84 * @param mixed $new_value New value.
85 */
86 public static function update_profile_option( $old_value, $new_value ) {
87 // Return early if we're not completing the profiler.
88 if (
89 ( isset( $old_value['completed'] ) && $old_value['completed'] ) ||
90 ! isset( $new_value['completed'] ) ||
91 ! $new_value['completed']
92 ) {
93 return;
94 }
95
96 self::run();
97 }
98
99 /**
100 * Init is continued via admin_init so that WC is loaded when the product
101 * query is used, otherwise the query generates a "0 = 1" in the WHERE
102 * condition and thus doesn't return any results.
103 */
104 public static function on_admin_init() {
105 add_action( 'activated_plugin', array( __CLASS__, 'run' ) );
106 add_action( 'deactivated_plugin', array( __CLASS__, 'run_on_deactivated_plugin' ), 10, 1 );
107 StoredStateSetupForProducts::admin_init();
108
109 // Pre-fetch stored state so it has the correct initial values.
110 self::get_stored_state();
111 }
112
113 /**
114 * An init hook is used here so that StoredStateSetupForProducts can set
115 * up a hook that gets triggered by action-scheduler - this is needed
116 * because the admin_init hook doesn't get triggered by WP Cron.
117 */
118 public static function on_init() {
119 StoredStateSetupForProducts::init();
120 }
121
122 /**
123 * Go through the specs and run them.
124 */
125 public static function run() {
126 $specs = RemoteInboxNotificationsDataSourcePoller::get_instance()->get_specs_from_data_sources();
127
128 if ( false === $specs || ! is_countable( $specs ) || count( $specs ) === 0 ) {
129 return;
130 }
131
132 $stored_state = self::get_stored_state();
133 $errors = array();
134
135 foreach ( $specs as $spec ) {
136 $error = SpecRunner::run_spec( $spec, $stored_state );
137 if ( isset( $error ) ) {
138 $errors[] = $error;
139 }
140 }
141
142 if ( count( $errors ) > 0 ) {
143 self::log_errors( $errors );
144 }
145 }
146
147 /**
148 * Set an option indicating that WooCommerce Admin has just been updated,
149 * run the specs, then clear that option. This lets the
150 * WooCommerceAdminUpdatedRuleProcessor trigger on WCA update.
151 */
152 public static function run_on_woocommerce_admin_updated() {
153 update_option( self::WCA_UPDATED_OPTION_NAME, true, false );
154
155 self::run();
156
157 update_option( self::WCA_UPDATED_OPTION_NAME, false, false );
158 }
159
160 /**
161 * Gets the stored state option, and does the initial set up if it doesn't
162 * already exist.
163 *
164 * @return object The stored state option.
165 */
166 public static function get_stored_state() {
167 $stored_state = get_option( self::STORED_STATE_OPTION_NAME );
168
169 if ( false === $stored_state || ! is_object( $stored_state ) ) {
170 $stored_state = new \stdClass();
171
172 $stored_state = StoredStateSetupForProducts::init_stored_state(
173 $stored_state
174 );
175
176 update_option(
177 self::STORED_STATE_OPTION_NAME,
178 $stored_state,
179 false
180 );
181 }
182
183 return $stored_state;
184 }
185
186 /**
187 * The deactivated_plugin hook happens before the option is updated
188 * (https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/plugin.php#L826)
189 * so this captures the deactivated plugin path and pushes it into the
190 * PluginsProvider.
191 *
192 * @param string $plugin Path to the plugin file relative to the plugins directory.
193 */
194 public static function run_on_deactivated_plugin( $plugin ) {
195 PluginsProvider::set_deactivated_plugin( $plugin );
196 self::run();
197 }
198
199 /**
200 * Update the stored state option.
201 *
202 * @param object $stored_state The stored state.
203 */
204 public static function update_stored_state( $stored_state ) {
205 update_option( self::STORED_STATE_OPTION_NAME, $stored_state, false );
206 }
207
208 /**
209 * Get the note. This is used to display localized note.
210 *
211 * @param Note $note_from_db The note object created from db.
212 *
213 * @return Note The note.
214 */
215 public static function get_note_from_db( $note_from_db ) {
216 if ( ! $note_from_db instanceof Note || get_user_locale() === $note_from_db->get_locale() ) {
217 return $note_from_db;
218 }
219 $specs = RemoteInboxNotificationsDataSourcePoller::get_instance()->get_specs_from_data_sources();
220 foreach ( $specs as $spec ) {
221 if ( $spec->slug !== $note_from_db->get_name() ) {
222 continue;
223 }
224 $locale = SpecRunner::get_locale( $spec->locales, true );
225 if ( null === $locale ) {
226 // No locale found, so don't update the note.
227 break;
228 }
229
230 $localized_actions = SpecRunner::get_actions( $spec );
231
232 // Manually copy the action id from the db to the localized action, since they were not being provided.
233 foreach ( $localized_actions as $localized_action ) {
234 $action = $note_from_db->get_action( $localized_action->name );
235 if ( $action ) {
236 $localized_action->id = $action->id;
237 }
238 }
239
240 $note_from_db->set_title( $locale->title );
241 $note_from_db->set_content( $locale->content );
242 $note_from_db->set_actions( $localized_actions );
243 }
244
245 return $note_from_db;
246 }
247
248 /**
249 * Add the debug tools to the WooCommerce debug tools (WooCommerce > Status > Tools).
250 *
251 * @param array $tools a list of tools.
252 *
253 * @return mixed
254 *
255 * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
256 */
257 public static function add_debug_tools( $tools ) {
258 // Check if the site has opted out of marketplace suggestions.
259 if ( get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) !== 'yes' ) {
260 return false;
261 }
262
263 $tools['refresh_remote_inbox_notifications'] = array(
264 'name' => __( 'Refresh Remote Inbox Notifications', 'woocommerce' ),
265 'button' => __( 'Refresh', 'woocommerce' ),
266 'desc' => __( 'This will refresh the remote inbox notifications', 'woocommerce' ),
267 'callback' => function () {
268 RemoteInboxNotificationsDataSourcePoller::get_instance()->read_specs_from_data_sources();
269 RemoteInboxNotificationsEngine::run();
270
271 return __( 'Remote inbox notifications have been refreshed', 'woocommerce' );
272 },
273 );
274
275 $tools['delete_inbox_notification'] = array(
276 'name' => __( 'Delete an Inbox Notification', 'woocommerce' ),
277 'button' => __( 'Delete', 'woocommerce' ),
278 'desc' => __( 'This will delete an inbox notification by slug', 'woocommerce' ),
279 'selector' => array(
280 'description' => __( 'Select an inbox notification to delete:', 'woocommerce' ),
281 'class' => 'wc-product-search',
282 'search_action' => 'woocommerce_json_inbox_notifications_search',
283 'name' => 'delete_inbox_notification_note_id',
284 'placeholder' => esc_attr__( 'Search for an inbox notification&hellip;', 'woocommerce' ),
285 ),
286 'callback' => function () {
287 check_ajax_referer( 'debug_action', '_wpnonce' );
288
289 if ( ! isset( $_GET['delete_inbox_notification_note_id'] ) ) {
290 return __( 'No inbox notification selected', 'woocommerce' );
291 }
292 $note_id = wc_clean( sanitize_text_field( wp_unslash( $_GET['delete_inbox_notification_note_id'] ) ) );
293 $note = Notes::get_note( $note_id );
294
295 if ( ! $note ) {
296 return __( 'Inbox notification not found', 'woocommerce' );
297 }
298
299 $note->delete( true );
300 return __( 'Inbox notification has been deleted', 'woocommerce' );
301 },
302 );
303
304 return $tools;
305 }
306
307 /**
308 * Add ajax action for remote inbox notification search.
309 *
310 * @return void
311 *
312 * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
313 */
314 public static function ajax_action_inbox_notification_search() {
315 global $wpdb;
316
317 check_ajax_referer( 'search-products', 'security' );
318
319 if ( ! isset( $_GET['term'] ) ) {
320 wp_send_json( array() );
321 }
322
323 $search = wc_clean( sanitize_text_field( wp_unslash( $_GET['term'] ) ) );
324 $results = $wpdb->get_results(
325 $wpdb->prepare(
326 "SELECT note_id, name FROM {$wpdb->prefix}wc_admin_notes WHERE name LIKE %s",
327 '%' . $wpdb->esc_like( $search ) . '%'
328 )
329 );
330 $rows = array();
331 foreach ( $results as $result ) {
332 $rows[ $result->note_id ] = $result->name;
333 }
334 wp_send_json( $rows );
335 }
336 }
337