PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.1 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 All 261 releases
← All changes | includes/admin/class-ph-admin-settings.php +284 -114 1.4.622.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,12 +36,23 @@
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' );
40 + $settings[] = include( 'settings/class-ph-settings-frontend.php' );
36 41 $settings[] = include( 'settings/class-ph-settings-emails.php' );
42 + $settings[] = include( 'settings/class-ph-settings-features.php' );
37 43 $settings[] = include( 'settings/class-ph-settings-licenses.php' );
38 - $settings[] = include( 'settings/class-ph-settings-add-ons.php' );
39 44
45 + // Only show demo data tab if demo data add on not active, tab not dismissed and if newly installed since 2021-04-13 00:00:00
46 + if (
47 + !class_exists('PH_Demo_Data') &&
48 + get_option( 'propertyhive_install_timestamp', '' ) >= 1618268400 &&
49 + get_option( 'propertyhive_hide_demo_data_tab', '' ) != 'yes'
50 + )
51 + {
52 + $settings[] = include( 'settings/class-ph-settings-demo-data.php' );
53 + }
54 +
40 55 self::$settings = apply_filters( 'propertyhive_get_settings_pages', $settings );
41 56 }
42 57 return self::$settings;
43 58 }
@@ -47,27 +62,23 @@
47 62 */
48 63 public static function save() {
49 64 global $current_section, $current_tab;
50 65
51 - if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'propertyhive-settings' ) )
52 - die( __( 'Action failed. Please refresh the page and retry.', 'propertyhive' ) );
66 + if ( ! current_user_can( 'manage_options' ) ) {
67 + wp_die( esc_html__( 'Insufficient permissions', 'propertyhive' ), '', array( 'response' => 403 ) );
68 + }
53 69
70 + if ( empty( $_REQUEST['_wpnonce'] ) || ! is_string( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'propertyhive-settings' ) )
71 + die( esc_html(__( 'Action failed. Please refresh the page and retry.', 'propertyhive' )) );
72 +
54 73 // Trigger actions
55 74 do_action( 'propertyhive_settings_save_' . $current_tab );
56 75 do_action( 'propertyhive_update_options_' . $current_tab );
57 76 do_action( 'propertyhive_update_options' );
58 77
59 - // Clear any unwanted data
60 - //ph_delete_property_transients();
61 - //delete_transient( 'propertyhive_cache_excluded_uris' );
62 -
63 78 self::add_message( __( 'Your settings have been saved.', 'propertyhive' ) );
64 - //self::check_download_folder_protection();
65 79
66 - // Re-add endpoints and flush rules
67 - //PH()->query->init_query_vars();
68 - //PH()->query->add_endpoints();
69 - flush_rewrite_rules();
80 + update_option( 'propertyhive_queue_flush_rewrite_rules', 'yes' );
70 81
71 82 do_action( 'propertyhive_settings_saved' );
72 83 }
73 84
@@ -92,12 +103,32 @@
92 103 */
93 104 public static function show_messages() {
94 105 if ( sizeof( self::$errors ) > 0 ) {
95 106 foreach ( self::$errors as $error )
96 - echo '<div id="message" class="error fade"><p><strong>' . $error . '</strong></p></div>';
107 + {
108 + $allowed_tags = array(
109 + 'a' => array(
110 + 'href' => array(),
111 + ),
112 + );
113 +
114 + $error = wp_kses($error, $allowed_tags);
115 +
116 + echo '<div id="message" class="error fade"><p><strong>' . wp_kses( $error, $allowed_tags ) . '</strong></p></div>';
117 + }
97 118 } elseif ( sizeof( self::$messages ) > 0 ) {
98 119 foreach ( self::$messages as $message )
99 - echo '<div id="message" class="updated fade"><p><strong>' . $message . '</strong></p></div>';
120 + {
121 + $allowed_tags = array(
122 + 'a' => array(
123 + 'href' => array(),
124 + ),
125 + );
126 +
127 + $message = wp_kses($message, $allowed_tags);
128 +
129 + echo '<div id="message" class="updated fade"><p><strong>' . wp_kses( $message, $allowed_tags ) . '</strong></p></div>';
130 + }
100 131 }
101 132 }
102 133
103 134 /**
@@ -108,9 +139,9 @@
108 139 * @access public
109 140 * @return void
110 141 */
111 142 public static function output() {
112 - global $current_section, $current_tab;
143 + global $current_section, $current_tab, $redirect_after_save;
113 144
114 145 do_action( 'propertyhive_settings_start' );
115 146
116 147 //wp_enqueue_script( 'propertyhive_settings', PH()->plugin_url() . '/assets/js/admin/settings.min.js', array( 'jquery'/*, 'jquery-ui-datepicker', 'jquery-ui-sortable', 'iris', 'chosen'*/ ), PH()->version, true );
@@ -122,21 +153,32 @@
122 153 // Include settings pages
123 154 self::get_settings_pages();
124 155
125 156 // Get current tab/section
126 - $current_tab = empty( $_GET['tab'] ) ? 'general' : sanitize_title( $_GET['tab'] );
127 - $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'] ) : '';
128 165
129 166 // Save settings if data has been posted
130 - if ( ! empty( $_POST ) )
131 - self::save();
167 + //if ( ! empty( $_POST ) )
168 + // self::save();
132 169
133 170 // Add any posted messages
134 - if ( ! empty( $_GET['ph_error'] ) )
135 - 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 ) );
136 178
137 - if ( ! empty( $_GET['ph_message'] ) )
138 - 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 ) );
139 181
140 182 self::show_messages();
141 183
142 184 // Get tabs for the settings page
@@ -232,13 +274,13 @@
232 274 }
233 275
234 276 if ( $tip && in_array( $value['type'], array( 'checkbox' ) ) ) {
235 277
236 - $tip = '<p class="description">' . $tip . '</p>';
278 + $tip = '<p class="description">' . wp_kses_post( $tip ) . '</p>';
237 279
238 280 } elseif ( $tip ) {
239 281
240 - $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" />';
241 283
242 284 }
243 285
244 286 // Switch based on type
@@ -249,9 +291,9 @@
249 291 if ( ! empty( $value['title'] ) ) {
250 292 echo '<h3>' . esc_html( $value['title'] ) . '</h3>';
251 293 }
252 294 if ( ! empty( $value['desc'] ) ) {
253 - echo wpautop( wptexturize( wp_kses_post( $value['desc'] ) ) );
295 + echo wp_kses_post( wpautop( wptexturize( wp_kses_post( $value['desc'] ) ) ) );
254 296 }
255 297 echo '<table class="form-table">'. "\n\n";
256 298 if ( ! empty( $value['id'] ) ) {
257 299 do_action( 'propertyhive_settings_' . sanitize_title( $value['id'] ) );
@@ -269,16 +311,83 @@
269 311 }
270 312 break;
271 313
272 314 case 'html':
315 + $full_width = ( isset($value['full_width']) && is_bool($value['full_width']) ) ? $value['full_width'] : false;
273 316 ?>
274 317 <tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
318 + <?php if ( $full_width !== true ) { ?>
275 319 <th scope="row" class="titledesc">
276 320 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
277 - <?php echo $tip; ?>
321 + <?php echo wp_kses_post($tip); ?>
278 322 </th>
279 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
280 - <?php echo $value['html']; ?>
323 + <?php } ?>
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 + ?>
281 390 </td>
282 391 </tr>
283 392 <?php
284 393 break;
@@ -302,11 +411,11 @@
302 411
303 412 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
304 413 <th scope="row" class="titledesc">
305 414 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
306 - <?php echo $tip; ?>
415 + <?php echo wp_kses_post( $tip ); ?>
307 416 </th>
308 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
417 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
309 418 <input
310 419 name="<?php echo esc_attr( $value['id'] ); ?>"
311 420 id="<?php echo esc_attr( $value['id'] ); ?>"
312 421 type="<?php echo esc_attr( $type ); ?>"
@@ -312,10 +421,13 @@
312 421 type="<?php echo esc_attr( $type ); ?>"
313 422 style="<?php echo esc_attr( $value['css'] ); ?>"
314 423 value="<?php echo esc_attr( $option_value ); ?>"
315 424 class="<?php echo esc_attr( $value['class'] ); ?>"
316 - <?php echo implode( ' ', $custom_attributes ); ?>
317 - /> <?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 ); ?>
318 430 </td>
319 431 </tr><?php
320 432 break;
321 433
@@ -338,12 +450,12 @@
338 450
339 451 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
340 452 <th scope="row" class="titledesc">
341 453 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
342 - <?php echo $tip; ?>
454 + <?php echo wp_kses_post( $tip ); ?>
343 455 </th>
344 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
345 - <?php echo $description; ?>
456 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
457 + <?php echo wp_kses_post( $description ); ?>
346 458
347 459 <textarea
348 460 name="<?php echo esc_attr( $value['id'] ); ?>"
349 461 id="<?php echo esc_attr( $value['id'] ); ?>"
@@ -348,9 +460,12 @@
348 460 name="<?php echo esc_attr( $value['id'] ); ?>"
349 461 id="<?php echo esc_attr( $value['id'] ); ?>"
350 462 style="<?php echo esc_attr( $value['css'] ); ?>"
351 463 class="<?php echo esc_attr( $value['class'] ); ?>"
352 - <?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 + ?>
353 468 ><?php echo esc_textarea( $option_value ); ?></textarea>
354 469 </td>
355 470 </tr><?php
356 471 break;
@@ -362,20 +477,23 @@
362 477
363 478 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
364 479 <th scope="row" class="titledesc">
365 480 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
366 - <?php echo $tip; ?>
481 + <?php echo wp_kses_post( $tip ); ?>
367 482 </th>
368 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
483 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
369 484
370 485 <?php wp_editor( $option_value, esc_attr( $value['id'] ), array( 'media_buttons' => false, 'textarea_rows' => 3, 'teeny' => true ) ); ?>
371 486
372 - <?php echo '<br>' . $description; ?>
487 + <?php echo '<br>' . wp_kses_post( $description ); ?>
373 488
374 489 <?php /*<textarea
375 490 name="<?php echo esc_attr( $value['id'] ); ?>"
376 491 id="<?php echo esc_attr( $value['id'] ); ?>"
377 - <?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 + ?>
378 496 ><?php echo esc_textarea( $option_value ); ?></textarea>*/ ?>
379 497 </td>
380 498 </tr><?php
381 499 break;
@@ -388,17 +506,20 @@
388 506
389 507 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
390 508 <th scope="row" class="titledesc">
391 509 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
392 - <?php echo $tip; ?>
510 + <?php echo wp_kses_post( $tip ); ?>
393 511 </th>
394 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
512 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
395 513 <select
396 514 name="<?php echo esc_attr( $value['id'] ); ?><?php if ( $value['type'] == 'multiselect' ) echo '[]'; ?>"
397 515 id="<?php echo esc_attr( $value['id'] ); ?>"
398 516 style="<?php echo esc_attr( $value['css'] ); ?>"
399 517 class="<?php echo esc_attr( $value['class'] ); ?>"
400 - <?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 + ?>
401 522 <?php if ( $value['type'] == 'multiselect' ) echo 'multiple="multiple"'; ?>
402 523 >
403 524 <?php
404 525 foreach ( $value['options'] as $key => $val ) {
@@ -409,13 +530,13 @@
409 530 selected( in_array( $key, $option_value ), true );
410 531 else
411 532 selected( $option_value, $key );
412 533
413 - ?>><?php echo $val ?></option>
534 + ?>><?php echo esc_html( $val ); ?></option>
414 535 <?php
415 536 }
416 537 ?>
417 - </select> <?php echo $description; ?>
538 + </select> <?php echo wp_kses_post( $description ); ?>
418 539 </td>
419 540 </tr><?php
420 541 break;
421 542
@@ -426,13 +547,13 @@
426 547
427 548 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
428 549 <th scope="row" class="titledesc">
429 550 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
430 - <?php echo $tip; ?>
551 + <?php echo wp_kses_post( $tip ); ?>
431 552 </th>
432 - <td class="forminp forminp-<?php echo sanitize_title( $value['type'] ) ?>">
553 + <td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ) ?>">
433 554 <fieldset>
434 - <?php echo $description; ?>
555 + <?php echo wp_kses_post( $description ); ?>
435 556 <ul>
436 557 <?php
437 558 foreach ( $value['options'] as $key => $val ) {
438 559 ?>
@@ -438,15 +559,18 @@
438 559 ?>
439 560 <li>
440 561 <label><input
441 562 name="<?php echo esc_attr( $value['id'] ); ?>"
442 - value="<?php echo $key; ?>"
563 + value="<?php echo esc_attr( $key ); ?>"
443 564 type="radio"
444 565 style="<?php echo esc_attr( $value['css'] ); ?>"
445 566 class="<?php echo esc_attr( $value['class'] ); ?>"
446 - <?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 + ?>
447 571 <?php checked( $key, $option_value ); ?>
448 - /> <?php echo $val ?></label>
572 + /> <?php echo wp_kses_post( $val ); ?></label>
449 573 </li>
450 574 <?php
451 575 }
452 576 ?>
@@ -485,13 +609,13 @@
485 609 ?>
486 610 <tr valign="top" class="<?php echo esc_attr( implode( ' ', $visbility_class ) ); ?>" id="row_<?php echo esc_attr( $value['id'] ); ?>">
487 611 <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?></th>
488 612 <td class="forminp forminp-checkbox">
489 - <fieldset style="<?php echo $fieldset_css; ?>">
613 + <fieldset style="<?php echo esc_attr( $fieldset_css ); ?>">
490 614 <?php
491 615 } else {
492 616 ?>
493 - <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 ) ); ?>">
494 618 <?php
495 619 }
496 620
497 621 if ( ! empty( $value['title'] ) ) {
@@ -500,9 +624,9 @@
500 624 <?php
501 625 }
502 626
503 627 ?>
504 - <label for="<?php echo $value['id'] ?>">
628 + <label for="<?php echo esc_attr( $value['id'] ); ?>">
505 629 <input
506 630 name="<?php echo esc_attr( $name ); ?>"
507 631 id="<?php echo esc_attr( $value['id'] ); ?>"
508 632 type="checkbox"
@@ -507,11 +631,14 @@
507 631 id="<?php echo esc_attr( $value['id'] ); ?>"
508 632 type="checkbox"
509 633 value="1"
510 634 <?php checked( $option_value, 'yes'); ?>
511 - <?php echo implode( ' ', $custom_attributes ); ?>
512 - /> <?php echo $description ?>
513 - </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 ); ?>
514 641 <?php
515 642
516 643 if ( ! isset( $value['checkboxgroup'] ) || 'end' == $value['checkboxgroup'] ) {
517 644 ?>
@@ -530,22 +657,82 @@
530 657 case 'image_width' :
531 658
532 659 $width = self::get_option( $value['id'] . '[width]', $value['default']['width'] );
533 660 $height = self::get_option( $value['id'] . '[height]', $value['default']['height'] );
534 - $crop = checked( 1, self::get_option( $value['id'] . '[crop]', $value['default']['crop'] ), false );
535 661
536 662 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
537 - <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>
538 664 <td class="forminp image_width_settings">
539 665
540 - <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
541 667
542 - <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>
543 669
544 670 </td>
545 671 </tr><?php
546 672 break;
547 673
674 + // Image
675 + case 'image' :
676 +
677 + $option_value = self::get_option( $value['id'], $value['default'] );
678 +
679 + ?>
680 + <tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>_uploaded" <?php if ( $option_value == '' ) { echo ' style="display:none"'; } ?>>
681 + <th scope="row" class="titledesc"><?php echo esc_html( __( 'Uploaded', 'propertyhive' ) . ' ' . $value['title'] ); ?></th>
682 + <td class="forminp image_settings">
683 + <?php
684 + $image = wp_get_attachment_image_src( $option_value, 'thumbnail' );
685 + if ($image !== FALSE)
686 + {
687 + echo '<img src="' . esc_url( $image[0] ) . '" width="150" alt="">';
688 + }
689 + else
690 + {
691 + echo 'Image doesn\'t exist';
692 + }
693 + ?>
694 + </td>
695 + </tr>
696 + <tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
697 + <th scope="row" class="titledesc"><?php echo esc_html( $value['title'] ) ?> <?php echo wp_kses_post( $tip ); ?></th>
698 + <td class="forminp image_settings">
699 +
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 ); ?>" />
702 +
703 + </td>
704 + </tr><?php
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>';
733 + break;
734 +
548 735 // Single page selects
549 736 case 'single_select_page' :
550 737
551 738 $args = array( 'name' => $value['id'],
@@ -561,11 +748,13 @@
561 748 if( isset( $value['args'] ) )
562 749 $args = wp_parse_args( $value['args'], $args );
563 750
564 751 ?><tr valign="top" class="single_select_page" id="row_<?php echo esc_attr( $value['id'] ); ?>">
565 - <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>
566 753 <td class="forminp">
567 - <?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 ); ?>
568 757 </td>
569 758 </tr><?php
570 759 break;
571 760
@@ -582,15 +771,15 @@
582 771 }
583 772 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
584 773 <th scope="row" class="titledesc">
585 774 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
586 - <?php echo $tip; ?>
775 + <?php echo wp_kses_post( $tip ); ?>
587 776 </th>
588 777 <td class="forminp">
589 778 <select name="<?php echo esc_attr( $value['id'] ); ?>" style="<?php echo esc_attr( $value['css'] ); ?>">
590 779 <?php PH()->countries->country_dropdown_options( $country ); ?>
591 780 </select>
592 - <?php echo $description; ?>
781 + <?php echo wp_kses_post( $description ); ?>
593 782 </td>
594 783 </tr><?php
595 784 break;
596 785
@@ -607,9 +796,9 @@
607 796 asort( $countries );
608 797 ?><tr valign="top" id="row_<?php echo esc_attr( $value['id'] ); ?>">
609 798 <th scope="row" class="titledesc">
610 799 <label for="<?php echo esc_attr( $value['id'] ); ?>"><?php echo esc_html( $value['title'] ); ?></label>
611 - <?php echo $tip; ?>
800 + <?php echo wp_kses_post( $tip ); ?>
612 801 </th>
613 802 <td class="forminp">
614 803 <select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="<?php echo esc_attr( $value['css'] ); ?>">
615 804 <?php
@@ -614,11 +803,11 @@
614 803 <select multiple="multiple" name="<?php echo esc_attr( $value['id'] ); ?>[]" style="<?php echo esc_attr( $value['css'] ); ?>">
615 804 <?php
616 805 if ( $countries )
617 806 foreach ( $countries as $key => $val )
618 - 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>';
619 808 ?>
620 - </select> <?php if ( $description ) echo $description; ?>
809 + </select> <?php if ( $description ) echo wp_kses_post( $description ); ?>
621 810 </td>
622 811 </tr><?php
623 812 break;
624 813
@@ -639,11 +828,18 @@
639 828 * @param array $options Opens array to output
640 829 * @return bool
641 830 */
642 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 +
643 836 if ( empty( $_POST ) )
644 837 return false;
645 838
839 + // The settings nonce and manage_options capability were verified above.
840 + $request_post = wp_unslash( $_POST );
841 +
646 842 // Options to update will be stored here
647 843 $update_options = array();
648 844
649 845 // Loop options and get values to save
@@ -661,9 +857,9 @@
661 857
662 858 // Standard types
663 859 case "checkbox" :
664 860
665 - if ( isset( $_POST[ $value['id'] ] ) ) {
861 + if ( isset( $request_post[ $value['id'] ] ) ) {
666 862 $option_value = 'yes';
667 863 } else {
668 864 $option_value = 'no';
669 865 }
@@ -672,10 +868,10 @@
672 868
673 869 case "textarea" :
674 870 case "wysiwyg" :
675 871
676 - if ( isset( $_POST[$value['id']] ) ) {
677 - $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'] ] ) );
678 874 } else {
679 875 $option_value = '';
680 876 }
681 877
@@ -690,10 +886,10 @@
690 886 case "single_select_page" :
691 887 case "single_select_country" :
692 888 case 'radio' :
693 889
694 - if ( isset( $_POST[$value['id']] ) ) {
695 - $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'] ] );
696 892 } else {
697 893 $option_value = '';
698 894 }
699 895
@@ -702,13 +898,17 @@
702 898 // Special types
703 899 case "multiselect" :
704 900 case "multi_select_countries" :
705 901
706 - // Get countries array
707 - if ( isset( $_POST[ $value['id'] ] ) )
708 - $selected_countries = array_map( 'ph_clean', array_map( 'stripslashes', (array) $_POST[ $value['id'] ] ) );
709 - else
710 - $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 + }
711 911
712 912 $option_value = $selected_countries;
713 913
714 914 break;
@@ -714,14 +914,15 @@
714 914 break;
715 915
716 916 case "image_width" :
717 917
718 - 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'] ) ) {
719 920
720 - $update_options[ $value['id'] ]['width'] = ph_clean( stripslashes( $_POST[ $value['id'] ]['width'] ) );
721 - $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'];
722 923
723 - if ( isset( $_POST[ $value['id'] ]['crop'] ) )
924 + if ( isset( $image_dimensions['crop'] ) )
724 925 $update_options[ $value['id'] ]['crop'] = 1;
725 926 else
726 927 $update_options[ $value['id'] ]['crop'] = 0;
727 928
@@ -778,38 +979,7 @@
778 979 update_option( $name, $value );
779 980
780 981 return true;
781 982 }
782 -
783 - /**
784 - * Checks which method we're using to serve downloads
785 - *
786 - * If using force or x-sendfile, this ensures the .htaccess is in place
787 - *
788 - * @access public
789 - * @return void
790 - */
791 - /*public static function check_download_folder_protection() {
792 - $upload_dir = wp_upload_dir();
793 - $downloads_url = $upload_dir['basedir'] . '/propertyhive_uploads';
794 - $download_method = get_option('propertyhive_file_download_method');
795 -
796 - if ( $download_method == 'redirect' ) {
797 -
798 - // Redirect method - don't protect
799 - if ( file_exists( $downloads_url . '/.htaccess' ) )
800 - unlink( $downloads_url . '/.htaccess' );
801 -
802 - } else {
803 -
804 - // Force method - protect, add rules to the htaccess file
805 - if ( ! file_exists( $downloads_url . '/.htaccess' ) ) {
806 - if ( $file_handle = @fopen( $downloads_url . '/.htaccess', 'w' ) ) {
807 - fwrite( $file_handle, 'deny from all' );
808 - fclose( $file_handle );
809 - }
810 - }
811 - }
812 - }*/
813 983 }
814 984
815 985 endif;