PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 1.4.62 All 260 releases
← All changes | includes/admin/class-ph-admin-settings.php +215 -130 2.2.32.3.0 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * PropertyHive Admin Settings Class.
4 7 *
5 8 * @author PropertyHive
@@ -14,8 +17,9 @@
14 17
15 18 /**
16 19 * PH_Admin_Settings
17 20 */
21 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Admin_Settings; preserving the existing PH_* class name is required for plugin and extension compatibility.
18 22 class PH_Admin_Settings {
19 23
20 24 private static $settings = array();
21 25 private static $errors = array();
@@ -32,14 +36,8 @@
32 36
33 37 $settings[] = include( 'settings/class-ph-settings-general.php' );
34 38 $settings[] = include( 'settings/class-ph-settings-offices.php' );
35 39 $settings[] = include( 'settings/class-ph-settings-custom-fields.php' );
36 - $propertyhive_template_assistant_auto_deactivated = get_option('propertyhive_template_assistant_auto_deactivated', '');
37 - if ( !empty($propertyhive_template_assistant_auto_deactivated) )
38 - {
39 - // Only show if they had the TA active and we deactived it. Don't want it showing for new users
40 - $settings[] = include( 'settings/class-ph-settings-template-assistant.php' ); // Maybe temporary after migrating TA code into core. Remove in future version
41 - }
42 40 $settings[] = include( 'settings/class-ph-settings-frontend.php' );
43 41 $settings[] = include( 'settings/class-ph-settings-emails.php' );
44 42 $settings[] = include( 'settings/class-ph-settings-features.php' );
45 43 $settings[] = include( 'settings/class-ph-settings-licenses.php' );
@@ -64,9 +62,13 @@
64 62 */
65 63 public static function save() {
66 64 global $current_section, $current_tab;
67 65
68 - if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'propertyhive-settings' ) )
66 + if ( ! current_user_can( 'manage_options' ) ) {
67 + wp_die( esc_html__( 'Insufficient permissions', 'propertyhive' ), '', array( 'response' => 403 ) );
68 + }
69 +
70 + if ( empty( $_REQUEST['_wpnonce'] ) || ! is_string( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'propertyhive-settings' ) )
69 71 die( esc_html(__( 'Action failed. Please refresh the page and retry.', 'propertyhive' )) );
70 72
71 73 // Trigger actions
72 74 do_action( 'propertyhive_settings_save_' . $current_tab );
@@ -110,9 +112,9 @@
110 112 );
111 113
112 114 $error = wp_kses($error, $allowed_tags);
113 115
114 - echo '<div id="message" class="error fade"><p><strong>' . $error . '</strong></p></div>';
116 + echo '<div id="message" class="error fade"><p><strong>' . wp_kses( $error, $allowed_tags ) . '</strong></p></div>';
115 117 }
116 118 } elseif ( sizeof( self::$messages ) > 0 ) {
117 119 foreach ( self::$messages as $message )
118 120 {
@@ -123,9 +125,9 @@
123 125 );
124 126
125 127 $message = wp_kses($message, $allowed_tags);
126 128
127 - echo '<div id="message" class="updated fade"><p><strong>' . $message . '</strong></p></div>';
129 + echo '<div id="message" class="updated fade"><p><strong>' . wp_kses( $message, $allowed_tags ) . '</strong></p></div>';
128 130 }
129 131 }
130 132 }
131 133
@@ -151,10 +153,16 @@
151 153 // Include settings pages
152 154 self::get_settings_pages();
153 155
154 156 // Get current tab/section
155 - $current_tab = empty( $_GET['tab'] ) ? 'general' : sanitize_title( $_GET['tab'] );
156 - $current_section = empty( $_REQUEST['section'] ) ? '' : sanitize_title( $_REQUEST['section'] );
157 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- These values only select the read-only settings view; settings writes are handled by save_fields() after the settings nonce and capability checks.
158 + $request_get = wp_unslash( $_GET );
159 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- These values only select the read-only settings view; settings writes are handled by save_fields() after the settings nonce and capability checks.
160 + $request_request = wp_unslash( $_REQUEST );
161 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Shared admin settings-view state; this global is intentionally used to control the common settings template and is not an arbitrary application global.
162 + $current_tab = ( isset( $request_get['tab'] ) && is_string( $request_get['tab'] ) && '' !== $request_get['tab'] ) ? sanitize_title( $request_get['tab'] ) : 'general';
163 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Shared admin settings-view state; this global is intentionally used to control the common settings template and is not an arbitrary application global.
164 + $current_section = ( isset( $request_request['section'] ) && is_string( $request_request['section'] ) ) ? sanitize_title( $request_request['section'] ) : '';
157 165
158 166 // Save settings if data has been posted
159 167 //if ( ! empty( $_POST ) )
160 168 // self::save();
@@ -159,13 +167,18 @@
159 167 //if ( ! empty( $_POST ) )
160 168 // self::save();
161 169
162 170 // Add any posted messages
163 - if ( ! empty( $_GET['ph_error'] ) )
164 - self::add_error( stripslashes( $_GET['ph_error'] ) );
171 + $message_allowed_tags = array(
172 + 'a' => array(
173 + 'href' => array(),
174 + ),
175 + );
176 + if ( isset( $request_get['ph_error'] ) && is_scalar( $request_get['ph_error'] ) && '' !== (string) $request_get['ph_error'] )
177 + self::add_error( wp_kses( (string) $request_get['ph_error'], $message_allowed_tags ) );
165 178
166 - if ( ! empty( $_GET['ph_message'] ) )
167 - self::add_message( stripslashes( $_GET['ph_message'] ) );
179 + if ( isset( $request_get['ph_message'] ) && is_scalar( $request_get['ph_message'] ) && '' !== (string) $request_get['ph_message'] )
180 + self::add_message( wp_kses( (string) $request_get['ph_message'], $message_allowed_tags ) );
168 181
169 182 self::show_messages();
170 183
171 184 // Get tabs for the settings page
@@ -261,13 +274,13 @@
261 274 }
262 275
263 276 if ( $tip && in_array( $value['type'], array( 'checkbox' ) ) ) {
264 277
265 - $tip = '<p class="description">' . $tip . '</p>';
278 + $tip = '<p class="description">' . wp_kses_post( $tip ) . '</p>';
266 279
267 280 } elseif ( $tip ) {
268 281
269 - $tip = '<img class="help_tip" data-tip="' . esc_attr( $tip ) . '" src="' . PH()->plugin_url() . '/assets/images/help.png" height="16" width="16" />';
282 + $tip = '<img class="help_tip" data-tip="' . esc_attr( wp_kses_post( $tip ) ) . '" src="' . esc_url( PH()->plugin_url() . '/assets/images/help.png' ) . '" height="16" width="16" />';
270 283
271 284 }
272 285
273 286 // Switch based on type
@@ -278,9 +291,9 @@
278 291 if ( ! empty( $value['title'] ) ) {
279 292 echo '<h3>' . esc_html( $value['title'] ) . '</h3>';
280 293 }
281 294 if ( ! empty( $value['desc'] ) ) {
282 - echo wpautop( wptexturize( wp_kses_post( $value['desc'] ) ) );
295 + echo wp_kses_post( wpautop( wptexturize( wp_kses_post( $value['desc'] ) ) ) );
283 296 }
284 297 echo '<table class="form-table">'. "\n\n";
285 298 if ( ! empty( $value['id'] ) ) {
286 299 do_action( 'propertyhive_settings_' . sanitize_title( $value['id'] ) );
@@ -304,13 +317,77 @@
304 317 <tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
305 318 <?php if ( $full_width !== true ) { ?>
306 319 <th scope="row" class="titledesc">
307 320 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
308 - <?php echo $tip; ?>
321 + <?php echo wp_kses_post($tip); ?>
309 322 </th>
310 323 <?php } ?>
311 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
312 - <?php echo $value['html']; ?>
324 + <td class="forminp forminp-<?php echo esc_attr(sanitize_title( $value['type'] )); ?>">
325 + <?php
326 + $allowed_html = wp_kses_allowed_html( 'post' );
327 +
328 + $allowed_html['fieldset'] = array(
329 + 'id' => true,
330 + 'class' => true,
331 + );
332 +
333 + $allowed_html['legend'] = array(
334 + 'class' => true,
335 + );
336 +
337 + $allowed_html['label'] = array(
338 + 'for' => true,
339 + 'class' => true,
340 + );
341 +
342 + $allowed_html['input'] = array(
343 + 'type' => true,
344 + 'name' => true,
345 + 'id' => true,
346 + 'value' => true,
347 + 'class' => true,
348 + 'style' => true,
349 + 'checked' => true,
350 + 'disabled' => true,
351 + 'placeholder' => true,
352 + );
353 +
354 + $allowed_html['select'] = array(
355 + 'name' => true,
356 + 'id' => true,
357 + 'class' => true,
358 + 'style' => true,
359 + 'multiple' => true,
360 + 'disabled' => true,
361 + );
362 +
363 + $allowed_html['option'] = array(
364 + 'value' => true,
365 + 'selected' => true,
366 + 'disabled' => true,
367 + );
368 +
369 + /**
370 + * Scripts are permitted for backward compatibility because existing
371 + * Property Hive extensions use HTML settings fields to output inline
372 + * administration scripts. To be revised in future after mentioned
373 + * extensions have been updated
374 + */
375 + $allowed_html['script'] = array(
376 + 'type' => true,
377 + 'src' => true,
378 + );
379 +
380 + $allowed_html['a']['data-department'] = true;
381 +
382 + $allowed_html = apply_filters(
383 + 'propertyhive_admin_settings_html_allowed_tags',
384 + $allowed_html,
385 + $value
386 + );
387 +
388 + echo wp_kses( $value['html'], $allowed_html );
389 + ?>
313 390 </td>
314 391 </tr>
315 392 <?php
316 393 break;
@@ -334,11 +411,11 @@
334 411
335 412 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
336 413 <th scope="row" class="titledesc">
337 414 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
338 - <?php echo $tip; ?>
415 + <?php echo wp_kses_post( $tip ); ?>
339 416 </th>
340 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
417 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
341 418 <input
342 419 name="<?php echo esc_attr( $value['id'] ); ?>"
343 420 id="<?php echo esc_attr( $value['id'] ); ?>"
344 421 type="<?php echo esc_attr( $type ); ?>"
@@ -344,10 +421,13 @@
344 421 type="<?php echo esc_attr( $type ); ?>"
345 422 style="<?php echo esc_attr( $value['css'] ); ?>"
346 423 value="<?php echo esc_attr( $option_value ); ?>"
347 424 class="<?php echo esc_attr( $value['class'] ); ?>"
348 - <?php echo implode( ' ', $custom_attributes ); ?>
349 - /> <?php echo $description; ?>
425 + <?php
426 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
427 + echo implode( ' ', $custom_attributes );
428 + ?>
429 + /> <?php echo wp_kses_post( $description ); ?>
350 430 </td>
351 431 </tr><?php
352 432 break;
353 433
@@ -370,12 +450,12 @@
370 450
371 451 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
372 452 <th scope="row" class="titledesc">
373 453 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
374 - <?php echo $tip; ?>
454 + <?php echo wp_kses_post( $tip ); ?>
375 455 </th>
376 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
377 - <?php echo $description; ?>
456 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
457 + <?php echo wp_kses_post( $description ); ?>
378 458
379 459 <textarea
380 460 name="<?php echo esc_attr( $value['id'] ); ?>"
381 461 id="<?php echo esc_attr( $value['id'] ); ?>"
@@ -380,9 +460,12 @@
380 460 name="<?php echo esc_attr( $value['id'] ); ?>"
381 461 id="<?php echo esc_attr( $value['id'] ); ?>"
382 462 style="<?php echo esc_attr( $value['css'] ); ?>"
383 463 class="<?php echo esc_attr( $value['class'] ); ?>"
384 - <?php echo implode( ' ', $custom_attributes ); ?>
464 + <?php
465 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
466 + echo implode( ' ', $custom_attributes );
467 + ?>
385 468 ><?php echo esc_textarea( $option_value ); ?></textarea>
386 469 </td>
387 470 </tr><?php
388 471 break;
@@ -394,20 +477,23 @@
394 477
395 478 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
396 479 <th scope="row" class="titledesc">
397 480 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
398 - <?php echo $tip; ?>
481 + <?php echo wp_kses_post( $tip ); ?>
399 482 </th>
400 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
483 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
401 484
402 485 <?php wp_editor( $option_value, esc_attr( $value['id'] ), array( 'media_buttons' => false, 'textarea_rows' => 3, 'teeny' => true ) ); ?>
403 486
404 - <?php echo '<br>' . $description; ?>
487 + <?php echo '<br>' . wp_kses_post( $description ); ?>
405 488
406 489 <?php /*<textarea
407 490 name="<?php echo esc_attr( $value['id'] ); ?>"
408 491 id="<?php echo esc_attr( $value['id'] ); ?>"
409 - <?php echo implode( ' ', $custom_attributes ); ?>
492 + <?php
493 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
494 + echo implode( ' ', $custom_attributes );
495 + ?>
410 496 ><?php echo esc_textarea( $option_value ); ?></textarea>*/ ?>
411 497 </td>
412 498 </tr><?php
413 499 break;
@@ -420,17 +506,20 @@
420 506
421 507 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
422 508 <th scope="row" class="titledesc">
423 509 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
424 - <?php echo $tip; ?>
510 + <?php echo wp_kses_post( $tip ); ?>
425 511 </th>
426 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
512 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
427 513 <select
428 514 name="<?php echo esc_attr( $value['id'] ); ?><?php if ( $value['type'] == 'multiselect' ) echo '[]'; ?>"
429 515 id="<?php echo esc_attr( $value['id'] ); ?>"
430 516 style="<?php echo esc_attr( $value['css'] ); ?>"
431 517 class="<?php echo esc_attr( $value['class'] ); ?>"
432 - <?php echo implode( ' ', $custom_attributes ); ?>
518 + <?php
519 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
520 + echo implode( ' ', $custom_attributes );
521 + ?>
433 522 <?php if ( $value['type'] == 'multiselect' ) echo 'multiple="multiple"'; ?>
434 523 >
435 524 <?php
436 525 foreach ( $value['options'] as $key => $val ) {
@@ -441,13 +530,13 @@
441 530 selected( in_array( $key, $option_value ), true );
442 531 else
443 532 selected( $option_value, $key );
444 533
445 - ?>><?php echo $val ?></option>
534 + ?>><?php echo esc_html( $val ); ?></option>
446 535 <?php
447 536 }
448 537 ?>
449 - </select> <?php echo $description; ?>
538 + </select> <?php echo wp_kses_post( $description ); ?>
450 539 </td>
451 540 </tr><?php
452 541 break;
453 542
@@ -458,13 +547,13 @@
458 547
459 548 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
460 549 <th scope="row" class="titledesc">
461 550 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
462 - <?php echo $tip; ?>
551 + <?php echo wp_kses_post( $tip ); ?>
463 552 </th>
464 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
553 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
465 554 <fieldset>
466 - <?php echo $description; ?>
555 + <?php echo wp_kses_post( $description ); ?>
467 556 <ul>
468 557 <?php
469 558 foreach ( $value['options'] as $key => $val ) {
470 559 ?>
@@ -470,15 +559,18 @@
470 559 ?>
471 560 <li>
472 561 <label><input
473 562 name="<?php echo esc_attr( $value['id'] ); ?>"
474 - value="<?php echo $key; ?>"
563 + value="<?php echo esc_attr( $key ); ?>"
475 564 type="radio"
476 565 style="<?php echo esc_attr( $value['css'] ); ?>"
477 566 class="<?php echo esc_attr( $value['class'] ); ?>"
478 - <?php echo implode( ' ', $custom_attributes ); ?>
567 + <?php
568 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
569 + echo implode( ' ', $custom_attributes );
570 + ?>
479 571 <?php checked( $key, $option_value ); ?>
480 - /> <?php echo $val ?></label>
572 + /> <?php echo wp_kses_post( $val ); ?></label>
481 573 </li>
482 574 <?php
483 575 }
484 576 ?>
@@ -517,13 +609,13 @@
517 609 ?>
518 610 <tr valign="top" class="<?php echo esc_attr( implode( ' ', $visbility_class ) ); ?>" id="row_<?php echo esc_attr( $value['id'] ); ?>">
519 611 <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?></th>
520 612 <td class="forminp forminp-checkbox">
521 - <fieldset style="<?php echo $fieldset_css; ?>">
613 + <fieldset style="<?php echo esc_attr( $fieldset_css ); ?>">
522 614 <?php
523 615 } else {
524 616 ?>
525 - <fieldset style="<?php echo $fieldset_css; ?>" class="<?php echo esc_attr( implode( ' ', $visbility_class ) ); ?>">
617 + <fieldset style="<?php echo esc_attr( $fieldset_css ); ?>" class="<?php echo esc_attr( implode( ' ', $visbility_class ) ); ?>">
526 618 <?php
527 619 }
528 620
529 621 if ( ! empty( $value['title'] ) ) {
@@ -532,9 +624,9 @@
532 624 <?php
533 625 }
534 626
535 627 ?>
536 - <label for="<?php echo $value['id'] ?>">
628 + <label for="<?php echo esc_attr( $value['id'] ); ?>">
537 629 <input
538 630 name="<?php echo esc_attr( $name ); ?>"
539 631 id="<?php echo esc_attr( $value['id'] ); ?>"
540 632 type="checkbox"
@@ -539,11 +631,14 @@
539 631 id="<?php echo esc_attr( $value['id'] ); ?>"
540 632 type="checkbox"
541 633 value="1"
542 634 <?php checked( $option_value, 'yes'); ?>
543 - <?php echo implode( ' ', $custom_attributes ); ?>
544 - /> <?php echo $description ?>
545 - </label> <?php echo $tip; ?>
635 + <?php
636 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Every custom attribute name and value is escaped when assembled above; retain trusted PHP settings attributes.
637 + echo implode( ' ', $custom_attributes );
638 + ?>
639 + /> <?php echo wp_kses_post( $description ); ?>
640 + </label> <?php echo wp_kses_post( $tip ); ?>
546 641 <?php
547 642
548 643 if ( ! isset( $value['checkboxgroup'] ) || 'end' == $value['checkboxgroup'] ) {
549 644 ?>
@@ -562,17 +657,16 @@
562 657 case 'image_width' :
563 658
564 659 $width = self::get_option( $value['id'] . '[width]', $value['default']['width'] );
565 660 $height = self::get_option( $value['id'] . '[height]', $value['default']['height'] );
566 - $crop = checked( 1, self::get_option( $value['id'] . '[crop]', $value['default']['crop'] ), false );
567 661
568 662 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
569 - <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo $tip; ?></th>
663 + <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo wp_kses_post( $tip ); ?></th>
570 664 <td class="forminp image_width_settings">
571 665
572 - <input name="<?php echo esc_attr( $value['id'] ); ?>[width]" id="<?php echo esc_attr( $value['id'] ); ?>-width" type="text" size="3" value="<?php echo $width; ?>" /> &times; <input name="<?php echo esc_attr( $value['id'] ); ?>[height]" id="<?php echo esc_attr( $value['id'] ); ?>-height" type="text" size="3" value="<?php echo $height; ?>" />px
666 + <input name="<?php echo esc_attr( $value['id'] ); ?>[width]" id="<?php echo esc_attr( $value['id'] ); ?>-width" type="text" size="3" value="<?php echo esc_attr( $width ); ?>" /> &times; <input name="<?php echo esc_attr( $value['id'] ); ?>[height]" id="<?php echo esc_attr( $value['id'] ); ?>-height" type="text" size="3" value="<?php echo esc_attr( $height ); ?>" />px
573 667
574 - <label><input name="<?php echo esc_attr( $value['id'] ); ?>[crop]" id="<?php echo esc_attr( $value['id'] ); ?>-crop" type="checkbox" <?php echo $crop; ?> /> <?php _e( 'Hard Crop?', 'propertyhive' ); ?></label>
668 + <label><input name="<?php echo esc_attr( $value['id'] ); ?>[crop]" id="<?php echo esc_attr( $value['id'] ); ?>-crop" type="checkbox" <?php checked( 1, self::get_option( $value['id'] . '[crop]', $value['default']['crop'] ) ); ?> /> <?php esc_html_e( 'Hard Crop?', 'propertyhive' ); ?></label>
575 669
576 670 </td>
577 671 </tr><?php
578 672 break;
@@ -589,9 +683,9 @@
589 683 <?php
590 684 $image = wp_get_attachment_image_src( $option_value, 'thumbnail' );
591 685 if ($image !== FALSE)
592 686 {
593 - echo '<img src="' . $image[0] . '" width="150" alt="">';
687 + echo '<img src="' . esc_url( $image[0] ) . '" width="150" alt="">';
594 688 }
595 689 else
596 690 {
597 691 echo 'Image doesn\'t exist';
@@ -599,67 +693,44 @@
599 693 ?>
600 694 </td>
601 695 </tr>
602 696 <tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
603 - <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo $tip; ?></th>
697 + <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo wp_kses_post( $tip ); ?></th>
604 698 <td class="forminp image_settings">
605 699
606 - <a href="" class="button button-primary ph_upload_photo_button<?php echo esc_attr( $value['id'] ); ?>">Select Image</a>
607 - <input name="<?php echo esc_attr( $value['id'] ); ?>" id="<?php echo esc_attr( $value['id'] ); ?>" type="hidden" value="<?php echo $option_value; ?>" />
700 + <a href="" data-ph-image-field="<?php echo esc_attr( $value['id'] ); ?>" class="button button-primary ph_upload_photo_button<?php echo esc_attr( $value['id'] ); ?>">Select Image</a>
701 + <input name="<?php echo esc_attr( $value['id'] ); ?>" id="<?php echo esc_attr( $value['id'] ); ?>" type="hidden" value="<?php echo esc_attr( $option_value ); ?>" />
608 702
609 703 </td>
610 704 </tr><?php
611 - echo '<script>
612 -
613 - var file_frame' . $value['id'] . ';
614 -
615 - jQuery(document).ready(function()
616 - {
617 - jQuery(\'body\').on(\'click\', \'.ph_upload_photo_button' . $value['id'] . '\', function( event ){
618 -
619 - event.preventDefault();
620 -
621 - // If the media frame already exists, reopen it.
622 - if ( file_frame' . $value['id'] . ' ) {
623 - file_frame' . $value['id'] . '.open();
624 - return;
625 - }
626 -
627 - // Create the media frame.
628 - file_frame' . $value['id'] . ' = wp.media.frames.file_frame' . $value['id'] . ' = wp.media({
629 - title: jQuery( this ).data( \'uploader_title\' ),
630 - button: {
631 - text: jQuery( this ).data( \'uploader_button_text\' ),
632 - },
633 - multiple: false // Set to true to allow multiple files to be selected
634 - });
635 -
636 - // When an image is selected, run a callback.
637 - file_frame' . $value['id'] . '.on( \'select\', function() {
638 - var selection = file_frame' . $value['id'] . '.state().get(\'selection\');
639 -
640 - selection.map( function( attachment ) {
641 -
642 - attachment = attachment.toJSON();
643 -
644 - // Do something with attachment.id and/or attachment.url here
645 - console.log(attachment.url);
646 -
647 - // Add selected image to page
648 - //add_photo_attachment_to_grid(attachment);
649 -
650 - jQuery(\'#row_' . esc_attr( $value['id'] ) . '_uploaded\').show();
651 - jQuery(\'#row_' . esc_attr( $value['id'] ) . '_uploaded td\').html(\'<img src="\' + attachment.url + \'" width="150" alt="">\');
652 - jQuery(\'#' . esc_attr( $value['id'] ) . '\').val(attachment.id);
653 - });
654 - });
655 -
656 - // Finally, open the modal
657 - file_frame' . $value['id'] . '.open();
658 - });
659 - });
660 -
661 - </script>';
705 + echo '<script>
706 +(function(fieldId) {
707 + jQuery(function($) {
708 + $(document.body).on("click", "[data-ph-image-field]", function(event) {
709 + if ($(this).attr("data-ph-image-field") !== fieldId) { return; }
710 + event.preventDefault();
711 + var frameKey = "file_frame" + fieldId;
712 + var frame = wp.media.frames[frameKey] || window[frameKey];
713 + if (frame) { frame.open(); return; }
714 + frame = wp.media({
715 + title: $(this).data("uploader_title"),
716 + button: { text: $(this).data("uploader_button_text") },
717 + multiple: false
718 + });
719 + wp.media.frames[frameKey] = window[frameKey] = frame;
720 + frame.on("select", function() {
721 + frame.state().get("selection").map(function(attachment) {
722 + attachment = attachment.toJSON();
723 + var row = $(document.getElementById("row_" + fieldId + "_uploaded"));
724 + row.show().find("td").empty().append($("<img>", { src: attachment.url, width: 150, alt: "" }));
725 + $(document.getElementById(fieldId)).val(attachment.id);
726 + });
727 + });
728 + frame.open();
729 + });
730 + });
731 +})(' . wp_json_encode( (string) $value['id'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ');
732 +</script>';
662 733 break;
663 734
664 735 // Single page selects
665 736 case 'single_select_page' :
@@ -677,11 +748,13 @@
677 748 if( isset( $value['args'] ) )
678 749 $args = wp_parse_args( $value['args'], $args );
679 750
680 751 ?><tr valign="top" class="single_select_page" id="row_<?php echo esc_attr( $value['id'] ); ?>">
681 - <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo $tip; ?></th>
752 + <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo wp_kses_post( $tip ); ?></th>
682 753 <td class="forminp">
683 - <?php echo str_replace(' id=', " data-placeholder='" . __( 'Select a page&hellip;', 'propertyhive' ) . "' style='" . $value['css'] . "' class='" . $value['class'] . "' id=", wp_dropdown_pages( $args ) ); ?> <?php echo $description; ?>
754 + <?php
755 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_dropdown_pages() produces escaped select HTML; all inserted attribute values are escaped here and trusted core filters retain their HTML contract.
756 + echo str_replace(' id=', " data-placeholder='" . esc_attr__( 'Select a page&hellip;', 'propertyhive' ) . "' style='" . esc_attr( $value['css'] ) . "' class='" . esc_attr( $value['class'] ) . "' id=", wp_dropdown_pages( $args ) ); ?> <?php echo wp_kses_post( $description ); ?>
684 757 </td>
685 758 </tr><?php
686 759 break;
687 760
@@ -698,15 +771,15 @@
698 771 }
699 772 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
700 773 <th scope="row" class="titledesc">
701 774 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
702 - <?php echo $tip; ?>
775 + <?php echo wp_kses_post( $tip ); ?>
703 776 </th>
704 777 <td class="forminp">
705 778 <select name="<?php echo esc_attr( $value['id'] ); ?>" style="<?php echo esc_attr( $value['css'] ); ?>">
706 779 <?php PH()->countries->country_dropdown_options( $country ); ?>
707 780 </select>
708 - <?php echo $description; ?>
781 + <?php echo wp_kses_post( $description ); ?>
709 782 </td>
710 783 </tr><?php
711 784 break;
712 785
@@ -723,9 +796,9 @@
723 796 asort( $countries );
724 797 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
725 798 <th scope="row" class="titledesc">
726 799 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
727 - <?php echo $tip; ?>
800 + <?php echo wp_kses_post( $tip ); ?>
728 801 </th>
729 802 <td class="forminp">
730 803 <select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="<?php echo esc_attr( $value['css'] ); ?>">
731 804 <?php
@@ -730,11 +803,11 @@
730 803 <select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="<?php echo esc_attr( $value['css'] ); ?>">
731 804 <?php
732 805 if ( $countries )
733 806 foreach ( $countries as $key => $val )
734 - echo '<option value="' . esc_attr( $key ) . '" ' . selected( in_array( $key, $selections ), true, false ).'>' . $val['name'] . '</option>';
807 + echo '<option value="' . esc_attr( $key ) . '" ' . selected( in_array( $key, $selections ), true, false ).'>' . esc_html( $val['name'] ) . '</option>';
735 808 ?>
736 - </select> <?php if ( $description ) echo $description; ?>
809 + </select> <?php if ( $description ) echo wp_kses_post( $description ); ?>
737 810 </td>
738 811 </tr><?php
739 812 break;
740 813
@@ -755,11 +828,18 @@
755 828 * @param array $options Opens array to output
756 829 * @return bool
757 830 */
758 831 public static function save_fields( $options ) {
832 + if ( ! current_user_can( 'manage_options' ) || ! isset( $_REQUEST['_wpnonce'] ) || ! is_string( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'propertyhive-settings' ) ) {
833 + return;
834 + }
835 +
759 836 if ( empty( $_POST ) )
760 837 return false;
761 838
839 + // The settings nonce and manage_options capability were verified above.
840 + $request_post = wp_unslash( $_POST );
841 +
762 842 // Options to update will be stored here
763 843 $update_options = array();
764 844
765 845 // Loop options and get values to save
@@ -777,9 +857,9 @@
777 857
778 858 // Standard types
779 859 case "checkbox" :
780 860
781 - if ( isset( $_POST[ $value['id'] ] ) ) {
861 + if ( isset( $request_post[ $value['id'] ] ) ) {
782 862 $option_value = 'yes';
783 863 } else {
784 864 $option_value = 'no';
785 865 }
@@ -788,10 +868,10 @@
788 868
789 869 case "textarea" :
790 870 case "wysiwyg" :
791 871
792 - if ( isset( $_POST[$value['id']] ) ) {
793 - $option_value = wp_kses_post( trim( stripslashes( $_POST[ $value['id'] ] ) ) );
872 + if ( isset( $request_post[$value['id']] ) && is_scalar( $request_post[$value['id']] ) ) {
873 + $option_value = wp_kses_post( trim( $request_post[ $value['id'] ] ) );
794 874 } else {
795 875 $option_value = '';
796 876 }
797 877
@@ -806,10 +886,10 @@
806 886 case "single_select_page" :
807 887 case "single_select_country" :
808 888 case 'radio' :
809 889
810 - if ( isset( $_POST[$value['id']] ) ) {
811 - $option_value = sanitize_text_field( stripslashes( $_POST[ $value['id'] ] ) );
890 + if ( isset( $request_post[$value['id']] ) && is_scalar( $request_post[$value['id']] ) ) {
891 + $option_value = sanitize_text_field( $request_post[ $value['id'] ] );
812 892 } else {
813 893 $option_value = '';
814 894 }
815 895
@@ -818,13 +898,17 @@
818 898 // Special types
819 899 case "multiselect" :
820 900 case "multi_select_countries" :
821 901
822 - // Get countries array
823 - if ( isset( $_POST[ $value['id'] ] ) )
824 - $selected_countries = array_map( 'ph_clean', array_map( 'stripslashes', (array) $_POST[ $value['id'] ] ) );
825 - else
826 - $selected_countries = array();
902 + // Get countries array
903 + $selected_countries = array();
904 + if ( isset( $request_post[ $value['id'] ] ) ) {
905 + foreach ( (array) $request_post[ $value['id'] ] as $selected_country ) {
906 + if ( is_scalar( $selected_country ) ) {
907 + $selected_countries[] = ph_clean( $selected_country );
908 + }
909 + }
910 + }
827 911
828 912 $option_value = $selected_countries;
829 913
830 914 break;
@@ -830,14 +914,15 @@
830 914 break;
831 915
832 916 case "image_width" :
833 917
834 - if ( isset( $_POST[$value['id'] ]['width'] ) ) {
918 + $image_dimensions = ( isset( $request_post[ $value['id'] ] ) && is_array( $request_post[ $value['id'] ] ) ) ? $request_post[ $value['id'] ] : array();
919 + if ( isset( $image_dimensions['width'] ) && is_scalar( $image_dimensions['width'] ) ) {
835 920
836 - $update_options[ $value['id'] ]['width'] = ph_clean( stripslashes( $_POST[ $value['id'] ]['width'] ) );
837 - $update_options[ $value['id'] ]['height'] = ph_clean( stripslashes( $_POST[ $value['id'] ]['height'] ) );
921 + $update_options[ $value['id'] ]['width'] = ph_clean( $image_dimensions['width'] );
922 + $update_options[ $value['id'] ]['height'] = ( isset( $image_dimensions['height'] ) && is_scalar( $image_dimensions['height'] ) ) ? ph_clean( $image_dimensions['height'] ) : $value['default']['height'];
838 923
839 - if ( isset( $_POST[ $value['id'] ]['crop'] ) )
924 + if ( isset( $image_dimensions['crop'] ) )
840 925 $update_options[ $value['id'] ]['crop'] = 1;
841 926 else
842 927 $update_options[ $value['id'] ]['crop'] = 0;
843 928