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-profile.php +121 -22 1.4.562.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 * Add extra profile fields for users in admin
4 7 *
5 8 * @author PropertyHive
@@ -16,8 +19,9 @@
16 19
17 20 /**
18 21 * PH_Admin_Profile Class.
19 22 */
23 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Admin_Profile; preserving the existing PH_* class name is required for plugin and extension compatibility.
20 24 class PH_Admin_Profile {
21 25
22 26 /**
23 27 * Hook in tabs.
@@ -58,24 +62,45 @@
58 62 }
59 63 }
60 64 wp_reset_postdata();
61 65
62 - $show_fields = apply_filters(
63 - 'propertyhive_user_meta_fields',
64 - array(
65 - 'negotiator' => array(
66 - 'title' => __( 'Additional Negotiator Information', 'propertyhive' ),
67 - 'fields' => array(
68 - 'office_id' => array(
69 - 'label' => __( 'Office', 'propertyhive' ),
70 - 'description' => '',
71 - 'type' => 'select',
72 - 'options' => array( '' => __( 'Select an office', 'property' ) ) + $offices,
73 - ),
74 - ),
75 - ),
76 - )
66 + $fields = array(
67 + 'office_id' => array(
68 + 'label' => __( 'Office', 'propertyhive' ),
69 + 'description' => '',
70 + 'type' => 'select',
71 + 'options' => array( '' => __( 'Select an office', 'propertyhive' ) ) + $offices,
72 + ),
73 + 'telephone_number' => array(
74 + 'label' => __( 'Telephone Number', 'propertyhive' ),
75 + 'description' => '',
76 + 'type' => 'text',
77 + ),
78 + 'photo_attachment_id' => array(
79 + 'label' => __( 'Photo', 'propertyhive' ),
80 + 'description' => '',
81 + 'type' => 'image',
82 + ),
77 83 );
84 +
85 + $user = wp_get_current_user();
86 + $roles = (array)$user->roles;
87 +
88 + $fields['crm_only_mode'] = array(
89 + 'label' => __( 'Property Hive-Only Mode', 'propertyhive' ),
90 + 'description' => __( 'Enabling this option will remove all top level WordPress menu items leaving just Property Hive options making it easier to navigate and use as a CRM', 'propertyhive' ),
91 + 'type' => in_array('administrator', $roles) ? 'checkbox' : 'hidden',
92 + );
93 +
94 + $show_fields = array(
95 + 'negotiator' => array(
96 + 'title' => __( 'Property Hive Negotiator Details', 'propertyhive' ),
97 + 'fields' => $fields,
98 + ),
99 + );
100 +
101 + $show_fields = apply_filters( 'propertyhive_user_meta_fields', $show_fields );
102 +
78 103 return $show_fields;
79 104 }
80 105
81 106 /**
@@ -99,11 +124,21 @@
99 124 $show_fields = $this->get_user_meta_fields();
100 125
101 126 foreach ( $show_fields as $fieldset_key => $fieldset ) :
102 127 ?>
103 - <h2><?php echo $fieldset['title']; ?></h2>
128 + <h2><?php echo esc_html($fieldset['title']); ?></h2>
104 129 <table class="form-table" id="<?php echo esc_attr( 'fieldset-' . $fieldset_key ); ?>">
105 130 <?php foreach ( $fieldset['fields'] as $key => $field ) : ?>
131 + <?php
132 + if ( ! empty( $field['type'] ) && 'hidden' === $field['type'] )
133 + {
134 + ?>
135 + <input type="hidden" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $this->get_user_meta( $user->ID, $key ) != '' ? $this->get_user_meta( $user->ID, $key ) : ( isset( $field['default'] ) ? $field['default'] : '' ) ); ?>" />
136 + <?php
137 + }
138 + else
139 + {
140 + ?>
106 141 <tr>
107 142 <th>
108 143 <label for="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $field['label'] ); ?></label>
109 144 </th>
@@ -108,9 +143,9 @@
108 143 <label for="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $field['label'] ); ?></label>
109 144 </th>
110 145 <td>
111 146 <?php if ( ! empty( $field['type'] ) && 'select' === $field['type'] ) : ?>
112 - <select name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" class="<?php echo esc_attr( $field['class'] ); ?>" style="width: 25em;">
147 + <select name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" class="<?php echo ( isset($field['class']) ? esc_attr($field['class']) : '' ); ?>" style="width: 25em;">
113 148 <?php
114 149 $selected = esc_attr( get_user_meta( $user->ID, $key, true ) );
115 150 foreach ( $field['options'] as $option_key => $option_value ) :
116 151 ?>
@@ -117,17 +152,72 @@
117 152 <option value="<?php echo esc_attr( $option_key ); ?>" <?php selected( $selected, $option_key, true ); ?>><?php echo esc_html( $option_value ); ?></option>
118 153 <?php endforeach; ?>
119 154 </select>
120 155 <?php elseif ( ! empty( $field['type'] ) && 'checkbox' === $field['type'] ) : ?>
121 - <input type="checkbox" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="1" class="<?php echo esc_attr( $field['class'] ); ?>" <?php checked( (int) get_user_meta( $user->ID, $key, true ), 1, true ); ?> />
156 + <input type="checkbox" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="1" class="<?php echo isset($field['class']) ? esc_attr( $field['class'] ) : ''; ?>" <?php checked( (int) get_user_meta( $user->ID, $key, true ), 1, true ); ?> />
157 + <?php elseif ( ! empty( $field['type'] ) && 'color' === $field['type'] ) : ?>
158 + <input type="color" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $this->get_user_meta( $user->ID, $key ) != '' ? $this->get_user_meta( $user->ID, $key ) : ( isset( $field['default'] ) ? $field['default'] : '' ) ); ?>" class="<?php echo esc_attr( $field['class'] ); ?>" />
122 159 <?php elseif ( ! empty( $field['type'] ) && 'button' === $field['type'] ) : ?>
123 160 <button type="button" id="<?php echo esc_attr( $key ); ?>" class="button <?php echo esc_attr( $field['class'] ); ?>"><?php echo esc_html( $field['text'] ); ?></button>
124 - <?php else : ?>
161 + <?php elseif ( ! empty( $field['type'] ) && 'image' === $field['type'] ) : ?>
162 + <input type="hidden" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" class="photo-attachment-id" value="<?php echo esc_attr( $this->get_user_meta( $user->ID, $key ) ); ?>" />
163 + <div class="photo-attachment-image">
164 + <?php if ( !empty($this->get_user_meta( $user->ID, $key )) ) { echo wp_get_attachment_image( $this->get_user_meta( $user->ID, $key ), 'thumbnail'); } ?>
165 + </div>
166 + <div class="wp-media-buttons">
167 + <button class="button propertyhive-add-media" id="propertyhive-add-media"><?php echo esc_html(__('Select', 'propertyhive')); ?></button>
168 + </div>
169 + <script>
170 +
171 + var file_frame;
172 +
173 + jQuery(document).ready(function()
174 + {
175 + jQuery('body').on('click', '.propertyhive-add-media', function( event ){
176 +
177 + event.preventDefault();
178 +
179 + // If the media frame already exists, reopen it.
180 + if ( file_frame ) {
181 + file_frame.open();
182 + return;
183 + }
184 +
185 + // Create the media frame.
186 + file_frame = wp.media.frames.file_frame = wp.media({
187 + multiple: false
188 + });
189 +
190 + // When an image is selected, run a callback.
191 + file_frame.on( 'select', function() {
192 + var selection = file_frame.state().get('selection');
193 +
194 + selection.map( function( attachment ) {
195 +
196 + attachment = attachment.toJSON();
197 +
198 + jQuery('#<?php echo esc_attr( $key ); ?>').val(attachment.id);
199 +
200 + var photo_html = '<img src="' + attachment.url + '" style="max-width:150px; max-height:150px;" alt=""></li>';
201 +
202 + jQuery('.photo-attachment-image').html(photo_html);
203 + });
204 + });
205 +
206 + // Finally, open the modal
207 + file_frame.open();
208 + });
209 + });
210 + </script>
211 + <?php else: ?>
125 212 <input type="text" name="<?php echo esc_attr( $key ); ?>" id="<?php echo esc_attr( $key ); ?>" value="<?php echo esc_attr( $this->get_user_meta( $user->ID, $key ) ); ?>" class="<?php echo ( ! empty( $field['class'] ) ? esc_attr( $field['class'] ) : 'regular-text' ); ?>" />
126 213 <?php endif; ?>
127 214 <p class="description"><?php echo wp_kses_post( $field['description'] ); ?></p>
128 215 </td>
129 216 </tr>
217 + <?php
218 + }
219 + ?>
130 220 <?php endforeach; ?>
131 221 </table>
132 222 <?php
133 223 endforeach;
@@ -139,13 +229,18 @@
139 229 * @param int $user_id User ID of the user being saved
140 230 */
141 231 public function save_extra_user_meta_fields( $user_id ) {
142 232
143 - if ( ! current_user_can( 'manage_propertyhive' ) ) {
233 + if ( ! current_user_can( 'manage_propertyhive' ) || ! current_user_can( 'edit_user', $user_id ) ) {
144 234 return;
145 235 }
146 236
147 - $user_meta = get_userdata($user_id);
237 + if ( ! isset( $_POST['_wpnonce'] ) || ! is_string( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-user_' . $user_id ) ) {
238 + return;
239 + }
240 +
241 + $user_meta = get_userdata($user_id);
242 + if ( ! $user_meta ) { return; }
148 243 $user_roles = $user_meta->roles;
149 244
150 245 if ( ! in_array("administrator", $user_roles) && ! in_array("editor", $user_roles) ) {
151 246 return;
@@ -159,9 +254,13 @@
159 254
160 255 if ( isset( $field['type'] ) && 'checkbox' === $field['type'] ) {
161 256 update_user_meta( $user_id, $key, isset( $_POST[ $key ] ) );
162 257 } elseif ( isset( $_POST[ $key ] ) ) {
163 - update_user_meta( $user_id, $key, ph_clean( $_POST[ $key ] ) );
258 + if ( isset( $field['type'] ) && in_array( $field['type'], array( 'text', 'hidden', 'color', 'image', 'select' ), true ) && ! is_scalar( $_POST[ $key ] ) ) {
259 + continue;
260 + }
261 + // Metadata APIs expect slashed values; preserve literal backslashes after sanitizing.
262 + update_user_meta( $user_id, $key, wp_slash( ph_clean( wp_unslash( $_POST[ $key ] ) ) ) );
164 263 }
165 264 }
166 265 }
167 266 }