PluginProbe
Edit Flow / trunk
Edit Flow vtrunk
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
edit-flow / common / php / class-module.php

class-module.php in Edit Flow trunk, at common/php/class-module.php

727 lines 23.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base class for Edit Flow modules.
4 *
5 * @package EditFlow
6 */
7
8 if ( ! class_exists( 'EF_Module' ) ) {
9
10 /**
11 * Base class any Edit Flow module should extend.
12 */
13 class EF_Module {
14
15 /**
16 * Published post statuses.
17 *
18 * @var array
19 */
20 public $published_statuses = array(
21 'publish',
22 'future',
23 'private',
24 );
25
26 /**
27 * URL to the module directory.
28 *
29 * @var string
30 */
31 public $module_url;
32
33 /**
34 * Module data object.
35 *
36 * @var object
37 */
38 public $module;
39
40 /**
41 * Constructor.
42 */
43 public function __construct() {}
44
45 /**
46 * Returns whether the current module is enabled.
47 *
48 * @since 0.9.1
49 *
50 * @return bool True if the module is enabled, false otherwise.
51 */
52 public function is_enabled() {
53 return 'on' === $this->module->options->enabled;
54 }
55
56 /**
57 * Returns whether the module with the given name is enabled.
58 *
59 * @since 0.7
60 *
61 * @param string $slug Slug of the module to check.
62 * @return bool True if the module is enabled, false otherwise.
63 */
64 public function module_enabled( $slug ) {
65 global $edit_flow;
66
67 return isset( $edit_flow->$slug ) && $edit_flow->$slug->is_enabled();
68 }
69
70 /**
71 * Returns whether analytics has been enabled or not.
72 *
73 * It's only enabled if the site is a production WPVIP site.
74 *
75 * @since 0.10.0
76 *
77 * @return bool True if analytics is enabled, false otherwise.
78 */
79 public function is_analytics_enabled() {
80 // Check if the site is a production WPVIP site and only then enable it.
81 $is_analytics_enabled = $this->is_vip_site( true );
82
83 // Filter to disable it.
84 $is_analytics_enabled = apply_filters( 'ef_should_analytics_be_enabled', $is_analytics_enabled );
85
86 return $is_analytics_enabled;
87 }
88
89 /**
90 * Check if the site is a WPVIP site.
91 *
92 * @since 0.10.0
93 *
94 * @param bool $only_production Whether to only allow production sites to be considered WPVIP sites.
95 * @return bool True if it is a WPVIP site, false otherwise.
96 */
97 protected function is_vip_site( $only_production = false ) {
98 $is_vip_site = defined( 'VIP_GO_ENV' )
99 && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false
100 && defined( 'FILES_CLIENT_SITE_ID' );
101
102 if ( $only_production ) {
103 $is_vip_site = $is_vip_site && defined( 'VIP_GO_ENV' ) && 'production' === constant( 'VIP_GO_ENV' );
104 }
105
106 return $is_vip_site;
107 }
108
109 /**
110 * Gets an array of allowed post types for a module.
111 *
112 * @return array Post-type-slug => post-type-label.
113 */
114 public function get_all_post_types() {
115
116 $allowed_post_types = array(
117 'post' => __( 'Post', 'edit-flow' ),
118 'page' => __( 'Page', 'edit-flow' ),
119 );
120 $custom_post_types = $this->get_supported_post_types_for_module();
121
122 foreach ( $custom_post_types as $custom_post_type => $args ) {
123 $allowed_post_types[ $custom_post_type ] = $args->label;
124 }
125 return $allowed_post_types;
126 }
127
128 /**
129 * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over).
130 *
131 * For every post type that doesn't explicitly have the 'on' value, turn it 'off'.
132 * If add_post_type_support() has been used anywhere (legacy support), inherit the state.
133 *
134 * @since 0.7
135 *
136 * @param array $module_post_types Current state of post type options for the module.
137 * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar').
138 * @return array The setting for each post type, normalized based on rules.
139 */
140 public function clean_post_type_options( $module_post_types = array(), $post_type_support = null ) {
141 $normalized_post_type_options = array();
142 $all_post_types = array_keys( $this->get_all_post_types() );
143 foreach ( $all_post_types as $post_type ) {
144 if ( ( isset( $module_post_types[ $post_type ] ) && 'on' == $module_post_types[ $post_type ] ) || post_type_supports( $post_type, $post_type_support ) ) {
145 $normalized_post_type_options[ $post_type ] = 'on';
146 } else {
147 $normalized_post_type_options[ $post_type ] = 'off';
148 }
149 }
150 return $normalized_post_type_options;
151 }
152
153 /**
154 * Get all of the possible post types that can be used with a given module.
155 *
156 * @since 0.7.2
157 *
158 * @param object $module The full module.
159 * @return array An array of post type objects.
160 */
161 public function get_supported_post_types_for_module( $module = null ) {
162
163 $pt_args = array(
164 '_builtin' => false,
165 'public' => true,
166 );
167 $pt_args = apply_filters( 'edit_flow_supported_module_post_types_args', $pt_args, $module );
168 return get_post_types( $pt_args, 'objects' );
169 }
170
171 /**
172 * Collect all of the active post types for a given module.
173 *
174 * @since 0.7
175 *
176 * @param object $module Module's data.
177 * @return array All of the post types that are 'on'.
178 */
179 public function get_post_types_for_module( $module ) {
180
181 $post_types = array();
182 if ( isset( $module->options->post_types ) && is_array( $module->options->post_types ) ) {
183 foreach ( $module->options->post_types as $post_type => $value ) {
184 if ( 'on' == $value ) {
185 $post_types[] = $post_type;
186 }
187 }
188 }
189 return $post_types;
190 }
191
192 /**
193 * Get all of the currently available post statuses.
194 *
195 * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly.
196 *
197 * @since 0.7
198 *
199 * @return array All of the post statuses that aren't a published state.
200 */
201 public function get_post_statuses() {
202 global $edit_flow;
203
204 if ( $this->module_enabled( 'custom_status' ) ) {
205 return $edit_flow->custom_status->get_custom_statuses();
206 } else {
207 return $this->get_core_post_statuses();
208 }
209 }
210
211 /**
212 * Get core's 'draft' and 'pending' post statuses, but include our special attributes.
213 *
214 * @since 0.8.1
215 *
216 * @return array
217 */
218 protected function get_core_post_statuses() {
219
220 return array(
221 (object) array(
222 'name' => __( 'Draft', 'edit-flow' ),
223 'description' => '',
224 'slug' => 'draft',
225 'position' => 1,
226 ),
227 (object) array(
228 'name' => __( 'Pending Review', 'edit-flow' ),
229 'description' => '',
230 'slug' => 'pending',
231 'position' => 2,
232 ),
233 );
234 }
235
236 /**
237 * Gets the name of the default custom status. If custom statuses are disabled,
238 * returns 'draft'.
239 *
240 * @return string Name of the status.
241 */
242 public function get_default_post_status() {
243
244 // Check if custom status module is enabled.
245 $custom_status_module = EditFlow()->custom_status->module->options;
246
247 if ( 'on' == $custom_status_module->enabled ) {
248 return $custom_status_module->default_status;
249 } else {
250 return 'draft';
251 }
252 }
253
254 /**
255 * Filter to all posts with a given post status (can be a custom status or a built-in status) and optional custom post type.
256 *
257 * @since 0.7
258 *
259 * @param string $slug The slug for the post status to which to filter.
260 * @param string $post_type Optional post type to which to filter.
261 * @return string An edit.php link to all posts with the given post status and, optionally, the given post type.
262 */
263 public function filter_posts_link( $slug, $post_type = 'post' ) {
264 $filter_link = add_query_arg( 'post_status', $slug, get_admin_url( null, 'edit.php' ) );
265 if ( 'post' != $post_type && in_array( $post_type, get_post_types( '', 'names' ) ) ) {
266 $filter_link = add_query_arg( 'post_type', $post_type, $filter_link );
267 }
268 return $filter_link;
269 }
270
271 /**
272 * Enqueue any resources (CSS or JS) associated with datepicker functionality.
273 *
274 * @since 0.7
275 */
276 public function enqueue_datepicker_resources() {
277
278 wp_enqueue_script( 'jquery-ui-datepicker' );
279
280 // Build script dependencies. Add wp-data for Gutenberg integration if available.
281 $dependencies = array( 'jquery', 'jquery-ui-datepicker' );
282 if ( function_exists( 'use_block_editor_for_post' ) && use_block_editor_for_post( get_post() ) ) {
283 $dependencies[] = 'wp-data';
284 }
285
286 wp_enqueue_script( 'edit_flow-date_picker', EDIT_FLOW_URL . 'common/js/ef_date.js', $dependencies, EDIT_FLOW_VERSION, true );
287 wp_add_inline_script( 'edit_flow-date_picker', sprintf( 'var ef_week_first_day = %s;', wp_json_encode( get_option( 'start_of_week' ) ) ), 'before' );
288
289 // Now styles.
290 wp_enqueue_style( 'jquery-ui-datepicker', EDIT_FLOW_URL . 'common/css/jquery.ui.datepicker.css', array( 'wp-jquery-ui-dialog' ), EDIT_FLOW_VERSION, 'screen' );
291 wp_enqueue_style( 'jquery-ui-theme', EDIT_FLOW_URL . 'common/css/jquery.ui.theme.css', false, EDIT_FLOW_VERSION, 'screen' );
292 }
293
294 /**
295 * Checks for the current post type.
296 *
297 * @since 0.7
298 *
299 * @return string|null The post type we've found, or null if no post type.
300 */
301 public function get_current_post_type() {
302 global $post, $typenow, $pagenow, $current_screen;
303 // get_post() needs a variable.
304 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reading post type from REQUEST for context detection, not processing form data.
305 $post_id = isset( $_REQUEST['post'] ) ? (int) $_REQUEST['post'] : false;
306
307 if ( $post && $post->post_type ) {
308 $post_type = $post->post_type;
309 } elseif ( $typenow ) {
310 $post_type = $typenow;
311 } elseif ( $current_screen && ! empty( $current_screen->post_type ) ) {
312 $post_type = $current_screen->post_type;
313 } elseif ( isset( $_REQUEST['post_type'] ) ) {
314 $post_type = sanitize_key( $_REQUEST['post_type'] );
315 } elseif ( 'post.php' == $pagenow
316 && $post_id
317 && ! empty( get_post( $post_id )->post_type ) ) {
318 $post_type = get_post( $post_id )->post_type;
319 } elseif ( 'edit.php' == $pagenow && empty( $_REQUEST['post_type'] ) ) {
320 $post_type = 'post';
321 } else {
322 $post_type = null;
323 }
324 // phpcs:enable WordPress.Security.NonceVerification.Recommended
325
326 return $post_type;
327 }
328
329 /**
330 * Wrapper for the get_user_meta() function so we can replace it if we need to.
331 *
332 * @since 0.7
333 *
334 * @param int $user_id Unique ID for the user.
335 * @param string $key Key to search against.
336 * @param bool $string Whether or not to return just one value.
337 * @return string|bool|array Whatever the stored value was.
338 */
339 public function get_user_meta( $user_id, $key, $string = true ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound -- Legacy parameter name.
340
341 $response = null;
342 $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string );
343 if ( ! is_null( $response ) ) {
344 return $response;
345 }
346
347 return get_user_meta( $user_id, $key, $string );
348 }
349
350 /**
351 * Wrapper for the update_user_meta() function so we can replace it if we need to.
352 *
353 * @since 0.7
354 *
355 * @param int $user_id Unique ID for the user.
356 * @param string $key Key to search against.
357 * @param string|bool|array $value The value to store.
358 * @param string|bool|array $previous Previous value to replace.
359 * @return bool Whether we were successful in saving.
360 */
361 public function update_user_meta( $user_id, $key, $value, $previous = null ) {
362
363 $response = null;
364 $response = apply_filters( 'ef_update_user_meta', $response, $user_id, $key, $value, $previous );
365 if ( ! is_null( $response ) ) {
366 return $response;
367 }
368
369 return update_user_meta( $user_id, $key, $value, $previous );
370 }
371
372 /**
373 * Take a status and a message, JSON encode and print.
374 *
375 * @since 0.7
376 *
377 * @param string $status Whether it was a 'success' or an 'error'.
378 * @param string $message Optional message. SECURITY: the caller is responsible for
379 * escaping this. Several JS consumers insert it as HTML (e.g.
380 * via jQuery .html()), so pass only fixed/translated strings,
381 * esc_html()'d values, or trusted server-generated markup —
382 * never raw, unescaped user input.
383 * @param int $http_code HTTP response code.
384 */
385 protected function print_ajax_response( $status, $message = '', $http_code = 200 ) {
386 header( 'Content-type: application/json;' );
387 http_response_code( $http_code );
388 echo wp_json_encode(
389 array(
390 'status' => $status,
391 'message' => $message,
392 )
393 );
394 wp_die();
395 }
396
397 /**
398 * Whether or not the current page is a post management page.
399 *
400 * A post management page is where the module's functionality is actually
401 * needed, such as post editing pages (post.php, post-new.php) or post listing
402 * pages (edit.php) for supported post types.
403 *
404 * @since 0.7
405 * @since 0.10.0 Actually implemented instead of returning true. Renamed from
406 * is_whitelisted_functional_view().
407 *
408 * @see https://github.com/Automattic/Edit-Flow/issues/351
409 *
410 * @param string $module_name (Optional) Module name to check against.
411 * @return bool Whether the current page is a post management page for the module.
412 */
413 public function is_post_management_page( $module_name = null ) {
414 global $pagenow, $edit_flow;
415
416 // Only load on post editing and listing pages.
417 $functional_pages = [ 'post.php', 'post-new.php', 'edit.php' ];
418 if ( ! in_array( $pagenow, $functional_pages, true ) ) {
419 return false;
420 }
421
422 // Get the current post type.
423 $current_post_type = $this->get_current_post_type();
424 if ( ! $current_post_type ) {
425 return false;
426 }
427
428 // If a module name is specified, check if this post type is supported by that module.
429 if ( $module_name && isset( $edit_flow->modules->$module_name ) ) {
430 $module = $edit_flow->modules->$module_name;
431 $supported_post_types = $this->get_post_types_for_module( $module );
432 if ( ! in_array( $current_post_type, $supported_post_types, true ) ) {
433 return false;
434 }
435 }
436
437 return true;
438 }
439
440 /**
441 * Whether or not the current page is an Edit Flow settings view (either main or module).
442 *
443 * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug.
444 * If there's no module name specified, it will return true against all Edit Flow settings views.
445 *
446 * @since 0.7
447 *
448 * @param string $module_name Optional module name to check against.
449 * @return bool Return true if it is.
450 */
451 public function is_whitelisted_settings_view( $module_name = null ) {
452 global $pagenow, $edit_flow;
453
454 // All of the settings views are based on admin.php and a $_GET['page'] parameter.
455 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Checking page parameter for context, not processing form data.
456 if ( 'admin.php' != $pagenow || ! isset( $_GET['page'] ) ) {
457 return false;
458 }
459
460 // Load all of the modules that have a settings slug/ callback for the settings page.
461 foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
462 if ( isset( $mod_data->options->enabled ) && 'on' == $mod_data->options->enabled && $mod_data->configure_page_cb ) {
463 $settings_view_slugs[] = $mod_data->settings_slug;
464 }
465 }
466
467 // The current page better be in the array of registered settings view slugs.
468 if ( ! in_array( $_GET['page'], $settings_view_slugs, true ) ) {
469 return false;
470 }
471
472 if ( $module_name && $edit_flow->modules->$module_name->settings_slug !== $_GET['page'] ) {
473 return false;
474 }
475 // phpcs:enable WordPress.Security.NonceVerification.Recommended
476
477 return true;
478 }
479
480
481 /**
482 * This is a hack, Hack, HACK!!!
483 *
484 * Encode all of the given arguments as a serialized array, and then base64_encode.
485 * Used to store extra data in a term's description field.
486 *
487 * @since 0.7
488 *
489 * @param array $args The arguments to encode.
490 * @return string Arguments encoded in base64.
491 */
492 public function get_encoded_description( $args = array() ) {
493 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Required for term description storage.
494 return base64_encode( maybe_serialize( $args ) );
495 }
496
497 /**
498 * If given an encoded string from a term's description field,
499 * return an array of values. Otherwise, return the original string.
500 *
501 * @since 0.7
502 *
503 * @param string $string_to_unencode Possibly encoded string.
504 * @return array Array if string was encoded, otherwise the string as the 'description' field.
505 */
506 public function get_unencoded_description( $string_to_unencode ) {
507 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval.
508 $decoded = base64_decode( $string_to_unencode );
509
510 /*
511 * Edit Flow only ever stores a serialized array here (see get_encoded_description()),
512 * so forbid object instantiation during unserialisation. Without allowed_classes a
513 * crafted term description could trigger PHP object injection. is_serialized() mirrors
514 * maybe_unserialize()'s own gate, so a plain (unencoded) description still round-trips
515 * unchanged and the callers' is_array() checks keep working.
516 */
517 if ( is_serialized( $decoded ) ) {
518 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Hardened with allowed_classes => false; stored data is always a scalar array.
519 return unserialize( $decoded, array( 'allowed_classes' => false ) );
520 }
521
522 return $decoded;
523 }
524
525 /**
526 * Get the publicly accessible URL for the module based on the filename.
527 *
528 * @since 0.7
529 *
530 * @param string $file File path for the module.
531 * @return string Publicly accessible URL for the module.
532 */
533 public function get_module_url( $file ) {
534 $module_url = plugins_url( '/', $file );
535 return trailingslashit( $module_url );
536 }
537
538 /**
539 * Displays a list of users that can be selected!
540 *
541 * @since 0.7
542 *
543 * @todo Add pagination support for blogs with billions of users.
544 *
545 * @param array|null $selected Selected users.
546 * @param array|null $args Optional arguments for the form.
547 */
548 public function users_select_form( $selected = null, $args = null ) {
549
550 // Set up arguments.
551 $defaults = array(
552 'list_class' => 'ef-users-select-form',
553 'input_id' => 'ef-selected-users',
554 );
555 $parsed_args = wp_parse_args( $args, $defaults );
556 $list_class = $parsed_args['list_class'];
557 $input_id = $parsed_args['input_id'];
558
559 $args = array(
560 'capability' => 'publish_posts',
561 'fields' => array(
562 'ID',
563 'display_name',
564 'user_nicename',
565 'user_email',
566 ),
567 'orderby' => 'display_name',
568 );
569 $args = apply_filters( 'ef_users_select_form_get_users_args', $args );
570
571 $users = get_users( $args );
572
573 if ( ! is_array( $selected ) ) {
574 $selected = array();
575 }
576 ?>
577
578 <?php if ( ! empty( $users ) ) : ?>
579 <ul class="<?php echo esc_attr( $list_class ); ?>">
580 <?php
581 foreach ( $users as $user ) :
582 $checked = in_array( (int) $user->ID, array_map( 'intval', (array) $selected ), true );
583 ?>
584 <li>
585 <label for="<?php echo esc_attr( $input_id . '-' . $user->ID ); ?>">
586 <div class="ef-user-subscribe-actions">
587 <?php do_action( 'ef_user_subscribe_actions', $user->ID, $checked ); ?>
588 <input type="checkbox" id="<?php echo esc_attr( $input_id . '-' . $user->ID ); ?>" name="<?php echo esc_attr( $input_id ); ?>[]" value="<?php echo esc_attr( $user->ID ); ?>"
589 <?php
590 if ( $checked ) {
591 echo "checked='checked' ";
592 }
593 // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function.
594 if ( get_current_user_id() == $user->ID ) {
595 echo 'class="post_following_list-current_user" ';
596 }
597 ?>
598 />
599 </div>
600
601 <span class="ef-user_displayname"><?php echo esc_html( $user->display_name ); ?></span>
602 <?php
603 /**
604 * Filters the secondary user identifier shown in the notifications list.
605 *
606 * By default, shows user_nicename for unique identification without exposing email.
607 * Return user_email to show email addresses, or empty string to hide.
608 *
609 * @since 0.10.1
610 *
611 * @param string $identifier The secondary identifier to display.
612 * @param object $user The user object.
613 */
614 $secondary_identifier = apply_filters( 'ef_user_secondary_identifier', $user->user_nicename, $user );
615 if ( ! empty( $secondary_identifier ) ) :
616 ?>
617 <span class="ef-user_useremail"><?php echo esc_html( $secondary_identifier ); ?></span>
618 <?php endif; ?>
619 </label>
620 </li>
621 <?php endforeach; ?>
622 </ul>
623 <?php endif; ?>
624 <?php
625 }
626
627 /**
628 * Adds an array of capabilities to a role.
629 *
630 * @since 0.7
631 *
632 * @param string $role A standard WP user role like 'administrator' or 'author'.
633 * @param array $caps One or more user caps to add.
634 */
635 public function add_caps_to_role( $role, $caps ) {
636
637 // In some contexts, we don't want to add caps to roles.
638 if ( apply_filters( 'ef_kill_add_caps_to_role', false, $role, $caps ) ) {
639 return;
640 }
641
642 global $wp_roles;
643
644 if ( $wp_roles->is_role( $role ) ) {
645 $role = get_role( $role );
646 foreach ( $caps as $cap ) {
647 $role->add_cap( $cap );
648 }
649 }
650 }
651
652 /**
653 * Add settings help menus to our module screens if the values exist.
654 *
655 * Auto-registered in Edit_Flow::register_module().
656 *
657 * @since 0.7
658 */
659 public function action_settings_help_menu() {
660
661 $screen = get_current_screen();
662
663 if ( ! method_exists( $screen, 'add_help_tab' ) ) {
664 return;
665 }
666
667 if ( 'edit-flow_page_' . $this->module->settings_slug != $screen->id ) {
668 return;
669 }
670
671 // Make sure we have all of the required values for our tab.
672 if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) {
673 $screen->add_help_tab( $this->module->settings_help_tab );
674
675 if ( isset( $this->module->settings_help_sidebar ) ) {
676 $screen->set_help_sidebar( $this->module->settings_help_sidebar );
677 }
678 }
679 }
680
681 /**
682 * Upgrade the term descriptions for all of the terms in a given taxonomy.
683 *
684 * @param string $taxonomy The taxonomy to upgrade.
685 */
686 public function upgrade_074_term_descriptions( $taxonomy ) {
687 $args = array(
688 'hide_empty' => false,
689 );
690 // This is migration code, so it's being left as is for now.
691 // phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found
692 $terms = get_terms( $taxonomy, $args );
693 foreach ( $terms as $term ) {
694 // If we can detect that this term already follows the new scheme, let's skip it.
695 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval.
696 $maybe_serialized = base64_decode( $term->description );
697 if ( is_serialized( $maybe_serialized ) ) {
698 continue;
699 }
700
701 $description_args = array();
702 // This description has been JSON-encoded, so let's decode it.
703 if ( 0 === strpos( $term->description, '{' ) ) {
704 $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) );
705 $unencoded_array = json_decode( $string_to_unencode, true );
706 // Only continue processing if it actually was an array. Otherwise, set to the original string.
707 if ( is_array( $unencoded_array ) ) {
708 foreach ( $unencoded_array as $key => $value ) {
709 // html_entity_decode only works on strings but sometimes we store nested arrays.
710 if ( ! is_array( $value ) ) {
711 $description_args[ $key ] = html_entity_decode( $value, ENT_QUOTES );
712 } else {
713 $description_args[ $key ] = $value;
714 }
715 }
716 }
717 } else {
718 $description_args['description'] = $term->description;
719 }
720 $new_description = $this->get_encoded_description( $description_args );
721 wp_update_term( $term->term_id, $taxonomy, array( 'description' => $new_description ) );
722 }
723 }
724 }
725
726 }
727