PluginProbe
wpForo Forum / 3.2.0
wpForo Forum v3.2.0
3.2.0 3.1.7 3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 All 140 releases
wpforo / classes / Forms.php

Forms.php in wpForo Forum 3.2.0, at classes/Forms.php

1,665 lines 65.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\classes;
4
5 // Exit if accessed directly
6 if( ! defined( 'ABSPATH' ) ) exit;
7
8 class Forms {
9 public $default;
10 public $field_dir;
11 public $current;
12
13 public function __construct() {
14 $this->init_defaults();
15 }
16
17 private function init_defaults() {
18 $this->field_dir = '/wpforo/users/fields/';
19 $this->default = [
20 'label' => '',
21 'title' => '',
22 'name' => '',
23 'value' => '',
24 'values' => '',
25 'type' => 'text',
26 'placeholder' => '',
27 'description' => '',
28 'id' => '',
29 'class' => '',
30 'attributes' => '',
31 'isDefault' => 0,
32 'isWrapItem' => '',
33 'isLabelFirst' => '',
34 'isDisabled' => 0,
35 'isEditable' => 1,
36 'isRequired' => 0,
37 'isMultiChoice' => 0,
38 'isAllowedCustomValues' => 0,
39 'UGroupIdsFrontAddNewDefaultValues' => [],
40 'isOnlyForGuests' => 0,
41 'isRemovable' => 1,
42 'isSearchable' => 0,
43 'allowedGroupIds' => [],
44 'fileExtensions' => '',
45 'fileSize' => 1,
46 'minLength' => 0,
47 'maxLength' => 0,
48 'faIcon' => '',
49 'html' => '',
50 'varname' => '',
51 'template' => '',
52 'cantBeInactive' => [],
53 'canEdit' => [ 1 ],
54 'canView' => [ 1, 2, 3, 5 ],
55 'can' => '',
56 'isDisplayDefaultValues' => 0,
57 ];
58 }
59
60 public function fix_field( $field ) {
61 return wpforo_array_args_cast_and_merge( (array) $field, $this->default );
62 }
63
64 /**
65 * Form builder
66 * for form layout and field building
67 *
68 * @param array $fields associative multi-level array
69 *
70 * @return string form HTML
71 */
72 public function build( $fields ) {
73 if( empty( $fields ) ) return '';
74 $html = '';
75 foreach( $fields as $row_key => $row ) {
76 $row_class = "row-$row_key " . apply_filters( 'wpforo_row_classes', '', $row_key );
77 $html .= '<div class="wpf-tr ' . esc_attr( $row_class ) . '">';
78 foreach( $row as $col_key => $col ) {
79 $col_class_field = '';
80 foreach( $col as $field ) {
81 $col_class_field .= ' wpf-row-' . wpfval( $field, 'name' );
82 }
83 reset( $col );
84 $col_class = "row_$row_key-col_$col_key " . $col_class_field . ' ' . apply_filters( 'wpforo_col_classes', '', $row_key, $col_key );
85 $html .= '<div class="wpf-td wpfw-' . count( $row ) . ' ' . esc_attr( $col_class ) . '">';
86 foreach( $col as $field ) {
87 if( ! empty( $field ) ) $html .= $this->build_field( $field );
88 }
89 $html .= '</div>';
90 }
91 $html .= '<div class="wpf-cl"></div></div>';
92 }
93
94 return $html;
95 }
96
97 /**
98 * Builds field with input HTML and wrapper divs
99 *
100 * @param array $args field arguments
101 *
102 * @return string field HTML
103 */
104 public function build_field( $args ) {
105 if( is_string( $args ) ) $args = WPF()->member->get_field( $args );
106
107 $html = '';
108 if( ! is_array( $args ) || empty( $args ) ) return '';
109 $f = wpforo_parse_args( $args, $this->default );
110 $f = $this->prepare_args( $f );
111
112 //Get field input tag
113 $field_html = $this->field( $f );
114
115 //Wrapping field input
116 if( $f['template'] === 'register' ) {
117 if( $this->can_add( $f ) ) {
118 $html = $this->field_wrap_register( $field_html, $f );
119 }
120 } elseif( $f['template'] === 'account' ) {
121 if( $this->can_edit( $f ) ) {
122 $html = $this->field_wrap_account( $field_html, $f );
123 }
124 } elseif( $f['template'] === 'profile' ) {
125 if( $this->can_view( $f ) && $this->can_show_value( $f ) ) {
126 $f = $this->esc_field( $f );
127 $html = $this->field_wrap_profile( $field_html, $f );
128 }
129 } elseif( $f['template'] === 'members' ) {
130 if( $this->can_show_value( $f ) ) $html = $this->field_wrap_members( $field_html, $f );
131 } else {
132 $html = $this->field_wrap_default( $field_html, $f );
133 }
134
135 return $html;
136 }
137
138 /**
139 * Field input HTML builder
140 *
141 * @param array $f field arguments
142 *
143 * @return string field input HTML
144 */
145 public function field( $f ) {
146 $f = $this->prepare_field_args( $f );
147
148 $f = apply_filters( 'wpforo_form_field', $f );
149
150 switch( $f['type'] ) {
151 case 'url':
152 $field_html = $this->field_url( $f );
153 break;
154 case 'file':
155 $field_html = $this->field_file( $f );
156 break;
157 case 'html':
158 $field_html = $this->field_html( $f );
159 break;
160 case 'radio':
161 $field_html = $this->field_radio( $f );
162 break;
163 case 'select':
164 $field_html = $this->field_select( $f );
165 break;
166 case 'avatar':
167 $field_html = $this->field_avatar( $f );
168 break;
169 case 'textarea':
170 $field_html = $this->field_textarea( $f );
171 break;
172 case 'tinymce':
173 $field_html = $this->field_tinymce( $f );
174 break;
175 case 'password':
176 $field_html = $this->field_password( $f );
177 break;
178 case 'checkbox':
179 $field_html = $this->field_checkbox( $f );
180 break;
181 case 'usergroup':
182 $field_html = $this->field_usergroup( $f );
183 break;
184 case 'secondary_groups':
185 $field_html = $this->field_secondary_groups( $f );
186 break;
187 case 'user_nicename':
188 $field_html = $this->field_nicename( $f );
189 break;
190 case 'tel':
191 $field_html = $this->field_tel( $f );
192 break;
193 case 'autocomplete':
194 $field_html = $this->field_autocomplete( $f );
195 break;
196 default:
197 $field_html = $this->field_text( $f );
198 }
199
200 return apply_filters( 'wpforo_form_field_html', $field_html, $f );
201 }
202
203 /**
204 * Wraps input HTML for Registration form
205 *
206 * @param string $field_html input HTML
207 * @param array $f field arguments
208 *
209 * @return string wrapped field input HTML
210 */
211 public function field_wrap_register( $field_html, $f ) {
212 $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr(
213 $f['title']
214 ) . '">';
215 if( $f['type'] !== 'html' ) {
216 if( $f['label'] || $f['description'] ) {
217 $html .= '<div class="wpf-label-wrap">';
218 if( $f['label'] ) {
219 $html .= '<p class="wpf-label wpfcl-1">' . $f['label'] . $f['required_indicator'] . '</p>';
220 }
221 if( $f['description'] ) {
222 $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
223 }
224 $html .= '</div>';
225 }
226 $html .= '<div class="wpf-field-wrap">';
227 if( $f['faIcon'] ) {
228 $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
229 }
230 $html .= $field_html;
231 $html .= '</div>';
232 } else {
233 $html .= $field_html;
234 }
235 $html .= '<div class="wpf-field-cl"></div></div>';
236
237 return $html;
238 }
239
240 /**
241 * Wraps input HTML for Account form
242 *
243 * @param string $field_html input HTML
244 * @param array $f field arguments
245 *
246 * @return string wrapped field input HTML
247 */
248 public function field_wrap_account( $field_html, $f ) {
249 $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr(
250 $f['title']
251 ) . '">';
252 if( $f['type'] === 'html' ) {
253 $html .= $field_html;
254 } elseif( $f['name'] === 'user_login' ) {
255 $html .= '<div class="wpf-label-wrap">';
256 $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( (string) $f['label'] ) . '</p>';
257 $html .= '</div>';
258 $html .= '<div class="wpf-field-wrap">';
259 $html .= '<span class="wpf-username">' . $f['value'] . '</span>';
260 $html .= '</div>';
261 } elseif( $f['type'] !== 'password' && ! $f['isEditable'] && ! $this->can_moderate( $f ) && WPF()->current_user_groupid !== 1 ) {
262 $f = $this->esc_field( $f );
263 $html .= '<div class="wpf-label-wrap">';
264 $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( (string) $f['label'] ) . '</p>';
265 $html .= '</div>';
266 $html .= '<div class="wpf-field-wrap">';
267 $html .= '<span class="wpf-filed-value"><i class="' . esc_attr( $f['faIcon'] ) . '"></i> ' . $f['value'] . '</span>';
268 $html .= '</div>';
269 } else {
270 if( $f['label'] || $f['description'] ) {
271 $html .= '<div class="wpf-label-wrap">';
272 if( $f['label'] ) {
273 $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( (string) $f['label'] ) . $f['required_indicator'] . '</p>';
274 }
275 if( $f['description'] ) {
276 $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
277 }
278 $html .= '</div>';
279 }
280 $html .= '<div class="wpf-field-wrap">';
281 if( $f['faIcon'] ) {
282 $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
283 }
284 $html .= $field_html;
285 $html .= '</div>';
286 }
287 $html .= '<div class="wpf-field-cl"></div></div>';
288
289 return $html;
290 }
291
292 /**
293 * Wraps input HTML for Profile page
294 *
295 * @param string $field_html input HTML
296 * @param array $f field arguments
297 *
298 * @return string wrapped field input HTML
299 */
300 public function field_wrap_profile( $field_html, $f ) {
301
302 if( ! in_array( $f['type'], [ 'html', 'avatar' ] ) && ! ( $f['isRequired'] && $f['isDisplayDefaultValues'] ) && ( ! isset( $f['value'] ) || ( ! is_numeric(
303 $f['value']
304 ) && empty( $f['value'] ) ) ) ) {
305 return false;
306 }
307
308 $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr(
309 $f['title']
310 ) . '">';
311 if( $f['type'] !== 'html' ) {
312 if( ! $f['faIcon'] ) {
313 $f['faIcon'] = 'fas fa-address-card';
314 }
315 if( $f['label'] ) {
316 $html .= '<div class="wpf-label-wrap"><p class="wpf-label wpfcl-1"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i> ' . $f['label'] . '</p></div>';
317 }
318 if( $f['type'] === 'avatar' || ( isset( $f['value'] ) && ! empty( $f['value'] ) ) ) {
319 if( is_array( $f['value'] ) ) {
320 $html .= esc_html( implode( ', ', $f['value'] ) );
321 } else {
322 $f = $this->prepare_values( $f, WPF()->current_object['userid'] );
323 $html .= '<div class="wpf-field-wrap">';
324 $html .= ( is_scalar( $f['value'] ) && ! $this->is_display_value_safe_html( $f ) ) ? esc_html( (string) $f['value'] ) : $f['value'];
325 $html .= '</div>';
326 }
327 } else {
328 if( $default_values = wpforo_preg_grep_recursive( '#^\[.+?]$#isu', $f['values'] ) ) {
329 $_html = '';
330 foreach( $default_values as $default_key => $default_value ) {
331 if( ! is_array( $default_value ) ) {
332 $item = $this->build_item_value_lable( $default_value );
333 $_html .= $item['label'] . ', ';
334 } else {
335 $csv = '';
336 foreach( $default_value as $_default_value ) {
337 if( ! is_array( $_default_value ) ) {
338 $item = $this->build_item_value_lable( $_default_value );
339 $csv .= $item['label'] . ', ';
340 }
341 }
342 $_html .= trim( (string) $default_key ) . '( ' . trim( (string) $csv, ', ' ) . ' ), ';
343 }
344 }
345 $html .= sprintf( '<div class="wpf-field-wrap">%1$s</div>', trim( (string) $_html, ', ' ) );
346 } else {
347 return false;
348 }
349 }
350 } else {
351 $html .= $field_html;
352 }
353 $html .= '<div class="wpf-field-cl"></div></div>';
354
355 return $html;
356 }
357
358 /**
359 * Wraps input HTML for Members Search form
360 *
361 * @param string $field_html input HTML
362 * @param array $f field arguments
363 *
364 * @return string wrapped field input HTML
365 */
366 public function field_wrap_members( $field_html, $f ) {
367 $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '" title="' . esc_attr(
368 $f['title']
369 ) . '">';
370 if( $f['type'] === 'html' ) {
371 $html .= $field_html;
372 } else {
373 if( $f['label'] || $f['description'] ) {
374 $html .= '<div class="wpf-label-wrap">';
375 if( $f['label'] ) {
376 $html .= '<p class="wpf-label wpfcl-1">' . stripslashes( (string) $f['label'] ) . $f['required_indicator'] . '</p>';
377 }
378 if( $f['description'] ) {
379 $html .= '<div class="wpf-desc wpfcl-2">' . $f['description'] . '</div>';
380 }
381 $html .= '</div>';
382 }
383 $html .= '<div class="wpf-field-wrap">';
384 if( $f['faIcon'] ) {
385 $html .= '<i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
386 }
387 $html .= $field_html;
388 $html .= '</div>';
389 }
390 $html .= '<div class="wpf-field-cl"></div></div>';
391
392 return $html;
393 }
394
395 /**
396 * Default wrapper of input HTML
397 *
398 * @param string $field_html input HTML
399 * @param array $f field arguments
400 *
401 * @return string wrapped field input HTML
402 */
403 public function field_wrap_default( $field_html, $f ) {
404 $html = '<div class="wpf-field wpf-field-type-' . esc_attr( $f['type'] ) . ' wpf-field-name-' . esc_attr( $f['field_class'] ) . ' ' . esc_attr( $f['required_class'] ) . '">';
405 if( $f['type'] === 'html' ) {
406 $html .= $field_html;
407 } else {
408 if( $f['label'] || $f['description'] ) {
409 $html .= '<div class="wpf-label-wrap" style="float: none; width: 100%;">';
410 if( $f['faIcon'] ) $html .= '<i class="' . esc_attr( $f['faIcon'] ) . '"></i>';
411 if( $f['label'] ) {
412 $html .= '<p class="wpf-label wpfcl-1" style="display: inline-block; margin-left: 5px;">' . stripslashes( (string) $f['label'] ) . $f['required_indicator'] . '</p>';
413 }
414
415 if( wpfval( $f, 'type' ) === 'file' && ( $file_size = wpfval( $f, 'fileSize' ) ) ) {
416 $html .= '<em style="opacity: 0.8;"> (' . wpforo_phrase( 'max allowed file size', false, 'lower' ) . ' ' . $file_size . 'MB)</em>';
417 }
418
419 if( $f['description'] ) {
420 $html .= '<div class="wpf-desc wpfcl-2" style="display: inline-block; margin-left: 5px;">' . $f['description'] . '</div>';
421 }
422 $html .= '</div>';
423 } else {
424 if( $f['faIcon'] ) $html .= '<i class="' . esc_attr( $f['faIcon'] ) . '"></i>';
425 }
426 $html .= '<div class="wpf-field-wrap" style="width: 100%">';
427 $html .= $field_html;
428 $html .= '</div>';
429 }
430 $html .= '<div class="wpf-field-cl"></div></div>';
431
432 return $html;
433 }
434
435 /**
436 * File - Field builder
437 *
438 * @param array $f field arguments
439 *
440 * @return string field HTML
441 */
442 public function field_file( $f ) {
443 $field_html = '';
444 $extensions = '';
445 if( $f['fileExtensions'] ) {
446 foreach( $f['fileExtensions'] as $key => $ext ) {
447 if( strpos( (string) $ext, '.' ) === false ) {
448 $f['fileExtensions'][ $key ] = '.' . $ext;
449 }
450 }
451 $f['fileExtensions'] = implode( ', ', $f['fileExtensions'] );
452 if( $f['fileExtensions'] ) $extensions = ' accept="' . esc_attr( $f['fileExtensions'] ) . '" ';
453 }
454 if( $f['value'] ) {
455 $f['isRequired'] = '';
456 if( is_array( $f['value'] ) ) {
457 $file_name = (string) wpfval( $f['value'], 'filename' );
458 $f['value'] = (string) wpfval( $f['value'], 'fileurl' );
459 } else {
460 $file_name = basename( (string) $f['value'] );
461 }
462 $extension = pathinfo( $f['value'], PATHINFO_EXTENSION );
463 $f['value'] = wpforo_fix_upload_url( $f['value'] );
464
465 if( wpfval( WPF()->current_object['user'], 'userid' ) ) {
466 $delete = '?wpfaction=ucf_file_delete&foro_f=' . $f['name'] . '&foro_u=' . WPF()->current_object['user']['userid'];
467 $delete_url = wp_nonce_url( $delete, 'wpforo_delete_profile_field', 'foro_n' );
468 $delete_html = ' &nbsp;|&nbsp; <a href="' . $delete_url . '" title="' . wpforo_phrase( 'Delete this file', false ) . '" onclick="return confirm(\'' . wpforo_phrase(
469 'Are you sure you want to delete this file?',
470 false
471 ) . '\')"><i class="fas fa-times" style="color:#e86666; font-size:14px; line-height:14px;"></i></a>';
472 } else {
473 $delete_html = ' &nbsp;|&nbsp; <span data-fieldkey="' . $f['fieldKey'] . '" style="cursor: pointer;" class="wpforo-delete-custom-file" title="' . wpforo_phrase(
474 'Delete this file',
475 false
476 ) . '"><i class="fas fa-times" style="color:#e86666; font-size:14px; line-height:14px;"></i></span>';
477 }
478
479 if( $extension && wpforo_is_image( $extension ) ) {
480 $field_html .= '<div class="wpf-field-file-wrap" style="margin-bottom: 10px;">
481 <img src="' . esc_url_raw( $f['value'] ) . '" alt="' . esc_attr( $file_name ) . '" title="' . esc_attr( $file_name ) . '" style="max-width:100px; max-height:100px"/>
482 <div class="wpf-field-file-name">
483 <i class="fas fa-paperclip"></i>
484 <a href="' . esc_url_raw( $f['value'] ) . '" title="' . esc_attr( $file_name ) . '" target="_blank">' . esc_attr( $file_name ) . '</a>' . $delete_html . '
485 </div>
486 </div>';
487 } else {
488 $field_html .= '<div class="wpf-field-file-wrap" style="margin-bottom: 10px;">
489 <div class="wpf-field-file-name">
490 <i class="fas fa-paperclip"></i>
491 <a href="' . esc_url_raw( $f['value'] ) . '" title="' . esc_attr( $file_name ) . '" target="_blank">' . esc_attr( $file_name ) . '</a>' . $delete_html . '
492 </div>
493 </div>';
494 }
495 }
496 $field_html .= '<input type="file" name="' . esc_attr( $f['fieldName'] ) . '" value=""
497 id="' . esc_attr( $f['fieldId'] ) . '" class="' . esc_attr( $f['fieldId'] ) . '"
498 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $extensions . ' />';
499
500 return $field_html;
501 }
502
503 private function form_extra( $f ) {
504 $form_type = wpfval( $f, 'form_type' );
505 switch( $form_type ) {
506 case 'topic':
507 if( $forum = wpfval( $f, 'meta', 'forum' ) ) {
508 wpforo_topic_form_extra( (int) wpfval( $forum, 'forumid' ), (array) wpfval( $f, 'meta', 'values' ) );
509 }
510 break;
511 case 'post':
512 if( $topic = wpfval( $f, 'meta', 'topic' ) ) {
513 wpforo_reply_form_extra( $topic, (array) wpfval( $f, 'meta', 'values' ) );
514 }
515 break;
516 }
517 }
518
519 /**
520 * Textarea - Field builder
521 *
522 * @param array $f field arguments
523 *
524 * @return string field HTML
525 */
526 public function field_textarea( $f ) {
527 $field_html = '<textarea name="' . esc_attr( $f['fieldName'] ) . '"
528 id="' . esc_attr( $f['fieldId'] ) . '"
529 class="' . esc_attr( $f['fieldId'] ) . '"
530 placeholder="' . esc_attr( $f['placeholder'] ) . '"
531 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . '
532 ' . $f['minmax'] . ' data-isbody="' . intval( $f['fieldKey'] === 'body' ) . '">' . esc_textarea( (string) $f['value'] ) . '</textarea>';
533 if( $f['fieldKey'] === 'body' ) {
534 ob_start();
535 $this->form_extra( $f );
536 $field_html .= ob_get_clean();
537 }
538
539 return $field_html;
540 }
541
542 /**
543 * Textarea - Field builder
544 *
545 * @param array $f field arguments
546 *
547 * @return string field HTML
548 */
549 public function field_tinymce( $f ) {
550 WPF()->current_object['load_tinymce'] = true;
551 $f['isRequired'] = '';
552 $f['attributes'] .= ' data-wpforoeditor="tinymce" ';
553 if( empty( $f['wp_editor_settings'] ) ) $f['wp_editor_settings'] = WPF()->tpl->editor_buttons();
554 $f['wp_editor_settings'] = apply_filters( 'wpforo_form_field_tinymce_editor_settings', $f['wp_editor_settings'], $f );
555 wp_localize_script( 'wpforo-frontend-js', 'wpforo_editor_settings_' . esc_attr( $f['fieldId'] ), $f['wp_editor_settings'] );
556
557 return $this->field_textarea( $f );
558 }
559
560 /**
561 * Password - Field builder
562 *
563 * @param array $f field arguments
564 *
565 * @return string field HTML
566 */
567 public function field_password( $f ) {
568 $field_html = '';
569 if( $f['template'] === 'account' ) {
570 $f['isRequired'] = 0;
571 $f['label'] = wpforo_phrase( 'Old password', false );
572 $f['description'] = '';
573 $field_html .= '<input type="password"
574 name="' . esc_attr( $f['varname'] ) . '[old_pass]"
575 value="" id="' . esc_attr( $f['fieldId'] ) . '-old"
576 class="' . esc_attr( $f['fieldId'] ) . '"
577 placeholder="' . esc_attr( wpforo_phrase( 'Old password', false ) ) . '"
578 ' . $f['isDisabled'] . ' ' . $f['attributes'] . '/>
579 <i class="fas fa-eye-slash wpf-show-password"></i>';
580 }
581 $p1 = '1';
582 $p2 = '2';
583 if( ! empty( $f['varname'] ) ) {
584 $f['fieldName'] = $f['varname'] . '[' . $f['name'] . $p1 . ']';
585 } else {
586 $f['fieldName'] = $f['name'] . $p1;
587 }
588 if( $f['template'] === 'account' ) {
589 $f['label'] = wpforo_phrase( 'New', false ) . ' ' . wpforo_phrase( $f['label'], false, 'lower' );
590 $f['placeholder'] = wpforo_phrase( 'New', false ) . ' ' . wpforo_phrase( $f['placeholder'], false, 'lower' );
591 }
592 if( in_array( $f['template'], [ 'account', 'register' ], true ) ) {
593 if( $f['template'] === 'account' ) $field_html .= '<div style="position:relative"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
594 $field_html .= '<input type="password"
595 name="' . esc_attr( $f['fieldName'] ) . '"
596 value="" id="' . esc_attr( $f['fieldId'] ) . '-new1"
597 class="' . esc_attr( $f['fieldId'] ) . '"
598 placeholder="' . esc_attr( $f['placeholder'] ) . '"
599 ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
600 if( $f['template'] === 'register' ) {
601 $field_html .= '<i class="fas fa-eye-slash wpf-show-password"></i>';
602 }
603 if( $f['template'] === 'account' ) {
604 $field_html .= '</div>';
605 }
606 $f['label'] = wpforo_phrase( 'Confirm Password', false );
607 $f['placeholder'] = wpforo_phrase( 'Confirm Password', false );
608 $f['description'] = '';
609 $f['fieldName'] = ( ! empty( $f['varname'] ) ? $f['varname'] . '[' . $f['name'] . $p2 . ']' : $f['name'] . $p2 );
610 $field_html .= '<div style="position:relative"><i class="' . esc_attr( $f['faIcon'] ) . ' wpf-field-icon"></i>';
611 $field_html .= '<input type="password"
612 name="' . esc_attr( $f['fieldName'] ) . '"
613 value="" id="' . esc_attr( $f['fieldId'] ) . '-new2"
614 class="' . esc_attr( $f['fieldId'] ) . '"
615 placeholder="' . esc_attr( $f['placeholder'] ) . '"
616 ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
617 $field_html .= '</div>';
618 }
619
620 return $field_html;
621 }
622
623 /**
624 * Checkbox - Field builder
625 *
626 * @param array $f field arguments
627 *
628 * @return string field HTML
629 */
630 public function field_checkbox( $f ) {
631 $i = 0;
632 $field_html = '';
633 $f['value'] = $this->build_array_value( $f['value'], "\n" );
634 $f['values'] = $this->build_array_using_string_rows( $f['values'] );
635 if( ! empty( $f['values'] ) ) {
636 $item_field_name = $f['fieldName'] . '[]';
637 $use_default_selected = ( trim( (string) $f['isRequired'] ) && ! $f['value'] );
638 $f['isRequired'] = ( count( $f['values'] ) == 1 ) ? $f['isRequired'] : '';
639 $field_html .= '<input type="hidden" name="' . esc_attr( $f['fieldName'] ) . '" value="">';
640 foreach( $f['values'] as $row ) {
641 $item = $this->build_item_value_lable( $row );
642 $id = $f['fieldId'] . '_' . ++ $i;
643 $field_html .= '<div class="wpf-field-item">';
644 $item_html = '<input type="checkbox" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' checked ' : '' ) : $this->check( $item['value'], $f['value'] ) ) . '
645 name="' . esc_attr( $item_field_name ) . '"
646 value="' . esc_attr( $item['value'] ) . '"
647 id="' . esc_attr( $id ) . '"
648 class="wpf-input-checkbox ' . esc_attr( $f['class'] ) . '"
649 ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
650 $field_html .= $this->build_label( $item['label'], $item_html, $f, $id );
651 $field_html .= '</div>';
652 }
653 }
654
655 return $field_html;
656 }
657
658 /**
659 * Radio - Field builder
660 *
661 * @param array $f field arguments
662 *
663 * @return string field HTML
664 */
665 public function field_radio( $f ) {
666 $i = 0;
667 $field_html = '';
668 $f['value'] = $this->build_array_value( $f['value'], "\n" );
669 $f['values'] = $this->build_array_using_string_rows( $f['values'] );
670 $use_default_selected = ( trim( (string) $f['isRequired'] ) && ! $f['value'] );
671 if( ! empty( $f['values'] ) ) {
672 foreach( $f['values'] as $row ) {
673 $item = $this->build_item_value_lable( $row );
674 $id = $f['fieldId'] . '_' . ++ $i;
675 $field_html .= '<div class="wpf-field-item">';
676 $item_html = '<input type="radio" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' checked ' : '' ) : $this->check( $item['value'], $f['value'] ) ) . '
677 name="' . esc_attr( $f['fieldName'] ) . '"
678 value="' . esc_attr( $item['value'] ) . '"
679 id="' . esc_attr( $id ) . '"
680 class="wpf-input-radio ' . esc_attr( $f['class'] ) . '"
681 ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . ' />';
682 $field_html .= $this->build_label( $item['label'], $item_html, $f, $id );
683 $field_html .= '</div>';
684 }
685 }
686
687 return $field_html;
688 }
689
690 /**
691 * Radio - Field builder
692 *
693 * @param array $f field arguments
694 *
695 * @return string field HTML
696 */
697 public function field_select( $f ) {
698 $field_html = '';
699 $f['value'] = $this->build_array_value( $f['value'], "\n" );
700 $f['values'] = $this->build_array_using_string_rows( $f['values'] );
701 $use_default_selected = ( trim( (string) $f['isRequired'] ) && ! $f['value'] );
702 if( ! empty( $f['values'] ) ) {
703 $field_html .= '<select name="' . esc_attr( $f['fieldName'] ) . '"
704 id="' . esc_attr( $f['fieldId'] ) . '"
705 class="' . esc_attr( $f['class'] ) . '"
706 ' . $f['isMultiChoice'] . ' ' . $f['isDisabled'] . '
707 ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
708 if( ! $f['isRequired'] ) {
709 $field_html .= apply_filters( 'wpforo_form_field_select_first_option', '<option value="">' . wpforo_phrase( '--- Choose ---', false ) . '</option>', $f );
710 }
711 foreach( $f['values'] as $optgroup => $row ) {
712 if( is_array( $row ) && ! empty( $row ) ) {
713 $field_html .= '<optgroup label="' . esc_attr( $optgroup ) . '">';
714 foreach( $row as $sub_row ) {
715 $item = $this->build_item_value_lable( $sub_row );
716 $item['value'] = ( $f['name'] === 'timezone' ) ? str_replace( ' ', '_', $item['value'] ) : $item['value'];
717 $field_html .= '<option value="' . esc_attr( $item['value'] ) . '" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' selected ' : '' ) : $this->select(
718 $item['value'],
719 $f['value']
720 ) ) . '>' . esc_html( $item['label'] ) . '</option>';
721 }
722 $field_html .= '</optgroup>';
723 } else {
724 $item = $this->build_item_value_lable( $row );
725 $item['value'] = ( $f['name'] === 'timezone' ) ? str_replace( ' ', '_', $item['value'] ) : $item['value'];
726 $field_html .= '<option value="' . esc_attr( $item['value'] ) . '" ' . ( $use_default_selected ? ( $item['default_selected'] ? ' selected ' : '' ) : $this->select(
727 $item['value'],
728 $f['value']
729 ) ) . '>' . esc_html( $item['label'] ) . '</option>';
730 }
731 }
732 $field_html .= '</select>';
733 }
734
735 return $field_html;
736 }
737
738 public function generate_multi_item( $val, $dvalues = [], $save_button = false ) {
739 return sprintf(
740 '<div class="wpf-multi-item wpf-multi-item-known-%1$d"><span class="wpf-multi-item-value">%2$s</span>%3$s<span class="wpf-multi-item-action wpf-del" title="%4$s"><i class="fa-solid fa-xmark" title="%4$s"></i></span></div>',
741 (int) in_array( $val, $dvalues, true ),
742 $val,
743 ( $save_button ? sprintf(
744 '<span class="wpf-multi-item-action wpf-save-variant" title="%1$s"><i class="fa-regular fa-floppy-disk" title="%1$s"></i></span>',
745 wpforo_phrase( 'Add to pre-defined values', false )
746 ) : '' ),
747 wpforo_phrase( 'remove', false )
748 );
749 }
750
751 /**
752 * Radio - Field builder
753 *
754 * @param array $f field arguments
755 *
756 * @return string field HTML
757 */
758 public function field_autocomplete( $f ) {
759 $f['value'] = $this->build_array_value( $f['value'], "\n" );
760 $f['values'] = $this->build_array_using_string_rows( $f['values'] );
761 $save_button = (int) wpfval( $f, 'formid' ) > 0 && array_intersect( WPF()->current_user_groupids, (array) wpfval( $f, 'UGroupIdsFrontAddNewDefaultValues' ) );
762
763 return sprintf(
764 '<%1$s data-dbvariants="%16$s" data-formid="%2$d" data-fieldkey="%3$s">%4$s<input type="text" name="%5$s" list="%6$s" class="%7$s" value="%8$s" autocomplete="off" %9$s %10$s %11$s %12$s><span class="wpf-invalid-msg wpf-required">%13$s</span><span class="wpf-invalid-msg wpf-not-allowed-value">%14$s</span>%15$s</%1$s>',
765 ( $f['isMultiChoice'] ? 'wpf-multi-input' : 'wpf-autocomplete-input' ),
766 (int) wpfval( $f, 'formid' ),
767 $f['fieldKey'],
768 ( $f['isMultiChoice'] ? sprintf( '<template id="wpf-multi-item-template">%1$s</template>', $this->generate_multi_item( '', [], $save_button ) ) . implode(
769 '',
770 array_map( function( $val ) use ( $f, $save_button ) { return $this->generate_multi_item( $val, $f['values'], $save_button ); }, array_filter( $f['value'] ) )
771 ) : '' ),
772 esc_attr( $f['fieldName'] ),
773 esc_attr( $f['fieldId'] ),
774 esc_attr( $f['class'] ),
775 ( ! $f['isMultiChoice'] ? current( $f['value'] ) : '' ),
776 $f['isDisabled'],
777 $f['isRequired'],
778 $f['attributes'],
779 ( (int) $f['isAllowedCustomValues'] || $save_button ? 'allow-custom-values' : '' ),
780 wpforo_phrase( 'This Field is Required', false ),
781 wpforo_phrase( 'Wrong Value (This Field Allows Only Predefined Values)', false ),
782 ( ( $values = (array) wpfval( $f, 'values' ) ) ? sprintf(
783 '<datalist id="%1$s">%2$s</datalist>',
784 esc_attr( $f['fieldId'] ),
785 implode(
786 '',
787 array_map(
788 function( $value ) {
789 return sprintf( '<option value="%1$s"></option>', esc_attr( $value ) );
790 },
791 array_diff( $values, $f['value'] )
792 )
793 )
794 ) : '' ),
795 esc_attr( wp_json_encode( $f['values'] ) )
796 );
797 }
798
799 /**
800 * Usergroup - Field builder
801 *
802 * @param array $f field arguments
803 *
804 * @return string field HTML
805 */
806 public function field_usergroup( $f ) {
807 $field_html = '';
808 if( ! empty( $f['allowedGroupIds'] ) ) {
809 $field_html .= '<select name="' . esc_attr( $f['fieldName'] ) . '"
810 id="' . esc_attr( $f['fieldId'] ) . '"
811 class="' . esc_attr( $f['class'] ) . '"
812 ' . $f['isDisabled'] . ' ' . $f['isRequired'] . ' ' . $f['attributes'] . '>';
813
814 $field_html .= '<option value="">' . wpforo_phrase( '--- Choose ---', false ) . '</option>';
815 foreach( $f['allowedGroupIds'] as $groupid ) {
816 if( $f['value'] != 4 && $groupid == 4 ) continue;
817 if( $group = WPF()->usergroup->get_usergroup( $groupid ) ) {
818 $field_html .= '<option value="' . esc_attr( $groupid ) . '" ' . $this->select( $groupid, $f['value'] ) . '>' . $group['name'] . '</option>';
819 }
820 }
821 $field_html .= '</select>';
822 }
823
824 return $field_html;
825 }
826
827 public function field_secondary_groups( $f ) {
828 $field_html = '';
829 if( $groups = WPF()->usergroup->get_secondary_groups() ) {
830 $allowed_groupids = (array) wpfval( $f, 'allowedGroupIds' );
831 $i = 0;
832 $field_html .= '<input type="hidden" name="' . esc_attr( $f['fieldName'] ) . '" value="">';
833 foreach( $groups as $group ) {
834 if( in_array( $group['groupid'], [ 1, 2, 4 ] ) || ( $allowed_groupids && ! in_array( $group['groupid'], $allowed_groupids ) ) ) continue;
835 $id = $f['fieldId'] . '_' . ++ $i;
836 $field_html .= '<div class="wpf-field-item">';
837 $item_html = '<input type="checkbox" ' . $this->check( $group['groupid'], $f['value'] ) . '
838 name="' . esc_attr( $f['fieldName'] . '[]' ) . '"
839 value="' . esc_attr( $group['groupid'] ) . '"
840 id="' . esc_attr( $id ) . '"
841 class="wpf-input-checkbox ' . esc_attr( $f['class'] ) . '"
842 ' . $f['isDisabled'] . ' ' . $f['attributes'] . '>';
843 $field_html .= $this->build_label( $group['name'], $item_html, $f, $id );
844 $field_html .= '</div>';
845 }
846 }
847
848 return $field_html;
849 }
850
851 /**
852 * Avatar - Field builder
853 *
854 * @param array $f field arguments
855 *
856 * @return string field HTML
857 */
858 public function field_avatar( $f ) {
859 $protocol = is_ssl() ? 'https://' : 'http://';
860 $remote_url = ( $f['value'] && strpos( (string) $f['value'], 'wpforo/avatars' ) === false ) ? $f['value'] : '';
861 $field_html = '<ul>
862 <li>
863 <input ' . $f['isRequired'] . ' name="' . esc_attr(
864 $f['varname']
865 ) . '[avatar_type]" id="wpfat_gravatar" value="gravatar" ' . ( $f['value'] == '' || $f['value'] == null ? 'checked="checked"' : '' ) . ' type="radio" />&nbsp;
866 <label for="wpfat_gravatar">' . wpforo_phrase( 'Default avatar', false ) . '</label>
867 </li>
868 <li>
869 <input name="' . esc_attr( $f['varname'] ) . '[avatar_type]" id="wpfat_remote" value="remote" ' . ( $f['value'] && strpos(
870 (string) $f['value'],
871 'wpforo/avatars'
872 ) === false ? 'checked="checked"' : '' ) . ' type="radio" />&nbsp;
873 <label for="wpfat_remote">' . wpforo_phrase( 'Specify avatar by URL:', false ) . '</label>
874 <input autocomplete="off" name="' . esc_attr( $f['varname'] ) . '[avatar_url]" value="' . esc_url( preg_replace( '|^//|is', $protocol, (string) $remote_url ) ) . '" maxlength="990" data-wpfucf-minmaxlength="1,990" type="url" />
875 </li>';
876 if( ( wpfval( WPF()->current_object, 'template' ) && WPF()->current_object['template'] === 'register' ) || WPF()->usergroup->can( 'upa' ) ) {
877 if( strpos( (string) $f['value'], 'gravatar.com' ) === false && strpos( $f['value'], 'facebook.com' ) === false ) {
878 $url = $f['value'] . '?lm=' . time();
879 } else {
880 $url = '';
881 }
882 $field_html .= '<li>
883 <input name="' . esc_attr( $f['varname'] ) . '[avatar_type]" id="wpfat_custom" value="custom" type="radio" ' . ( ( strpos( $url, 'wpforo/avatars' ) !== false ) ? 'checked' : '' ) . ' />&nbsp;
884 <label for="wpfat_custom"> ' . wpforo_phrase( 'Upload an avatar', false ) . '</label>' . ( strpos( $url, 'wpforo/avatars' ) !== false ? '<br />
885 <img src="' . esc_url( (string) $url ) . '" class="wpf-custom-avatar-img" alt="avatar">' : '' ) . '&nbsp;
886 <input class="wpf-custom-avatar" name="avatar" type="file" />&nbsp;</li>';
887 }
888 $field_html .= '</ul> <script type="text/javascript">jQuery(document).ready(function($){$( "input[name=\'' . esc_attr(
889 $f['varname']
890 ) . '\[avatar_url\]\']" ).on("click", function(){$( "#wpfat_remote" ).prop(\'checked\', true);}); $( "input[name=\'avatar\']" ).on("click", function(){$( "#wpfat_custom" ).prop(\'checked\', true);});});</script>';
891
892 return $field_html;
893 }
894
895 /**
896 * HTML - Field builder
897 *
898 * @param array $f field arguments
899 *
900 * @return string field HTML
901 */
902 public function field_html( $f ) {
903 return stripslashes( do_shortcode( wpforo_apply_ucf_shortcode( (string) $f['html'] ) ) );
904 }
905
906 /**
907 * URL - Field builder
908 *
909 * @param array $f field arguments
910 *
911 * @return string field HTML
912 */
913 public function field_url( $f ) {
914 return '<input type="url" value="' . esc_url_raw( $f['value'] ) . '"
915 name="' . esc_attr( $f['fieldName'] ) . '"
916 id="' . esc_attr( $f['fieldId'] ) . '"
917 class="' . esc_attr( $f['class'] ) . '"
918 placeholder="' . esc_attr( $f['placeholder'] ) . '"
919 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
920 }
921
922 /**
923 * Nickname - Field builder
924 *
925 * @param array $f field arguments
926 *
927 * @return string field HTML
928 */
929 public function field_nicename( $f ) {
930 return '<input type="text" value="' . esc_attr( urldecode( (string) $f['value'] ) ) . '"
931 name="' . esc_attr( $f['fieldName'] ) . '"
932 id="' . esc_attr( $f['fieldId'] ) . '"
933 class="' . esc_attr( $f['class'] ) . '"
934 placeholder="' . esc_attr( $f['placeholder'] ) . '"
935 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
936 }
937
938 /**
939 * Text - Field builder
940 *
941 * @param array $f field arguments
942 *
943 * @return string field HTML
944 */
945 public function field_text( $f ) {
946 return '<input type="' . esc_attr( $f['type'] ) . '"
947 value="' . esc_attr( $f['value'] ) . '"
948 name="' . esc_attr( $f['fieldName'] ) . '"
949 id="' . esc_attr( $f['fieldId'] ) . '"
950 class="' . esc_attr( $f['class'] ) . '"
951 placeholder="' . esc_attr( $f['placeholder'] ) . '"
952 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '/>';
953 }
954
955 public function field_tel( $f ) {
956 return '<input type="' . esc_attr( $f['type'] ) . '"
957 value="' . esc_attr( $f['value'] ) . '"
958 name="' . esc_attr( $f['fieldName'] ) . '"
959 id="' . esc_attr( $f['fieldId'] ) . '"
960 class="' . esc_attr( $f['class'] ) . '"
961 placeholder="' . esc_attr( $f['placeholder'] ) . '"
962 ' . $f['isRequired'] . ' ' . $f['isDisabled'] . ' ' . $f['attributes'] . ' ' . $f['minmax'] . '
963 pattern="' . ( ! empty( $f['pattern'] ) ? $f['pattern'] : '[0-9]*' ) . '">';
964 }
965
966 /**
967 * Prepares displayed values for Profile fields
968 *
969 * @param array $f field arguments
970 * @param int $userid
971 *
972 * @return array prepared values
973 */
974 public function prepare_values( $f, $userid = 0 ) {
975 switch( $f['type'] ) {
976 case 'textarea':
977 $f['value'] = wpforo_kses( wpforo_decode( $f['value'] ) );
978 break;
979 case 'date':
980 // $f['value'] = wpforo_date($f['value'], 'date', false);
981 break;
982 case 'datetime':
983 $f['value'] = wpforo_date( $f['value'], 'datetime', false );
984 break;
985 case 'url':
986 $f['value'] = sprintf( '<a href="%1$s" target="_blank" rel="nofollow">%2$s</a>', esc_url( $f['value'] ), esc_html( $f['value'] ) );
987 break;
988 case 'email':
989 $f['value'] = sprintf( '<a href="mailto:%1$s" rel="nofollow">%2$s</a>', esc_attr( $f['value'] ), esc_html( $f['value'] ) );
990 break;
991 case 'tel':
992 $f['value'] = sprintf( '<a href="tel:%1$s" rel="nofollow">%2$s</a>', esc_attr( $f['value'] ), esc_html( $f['value'] ) );
993 break;
994 case 'file':
995 if( ! empty( $f['value'] ) ) {
996 if( is_array( $f['value'] ) ) {
997 $file_name = (string) wpfval( $f['value'], 'filename' );
998 $f['value'] = (string) wpfval( $f['value'], 'fileurl' );
999 } else {
1000 $file_name = basename( (string) $f['value'] );
1001 }
1002 $f['value'] = wpforo_fix_upload_url( $f['value'] );
1003 $file_url = esc_url_raw( $f['value'] );
1004 $file_name = esc_attr( $file_name );
1005 $extension = pathinfo( $f['value'], PATHINFO_EXTENSION );
1006 if( wpforo_is_image( $extension ) ) {
1007 $f['value'] = sprintf(
1008 '<a href="%1$s" target="_blank" title="%2$s"><img src="%1$s" alt="%2$s" class="wpf-field-file-img" style="max-width:120px; max-height:120px"></a>',
1009 esc_url( $file_url ),
1010 esc_attr( $file_name )
1011 );
1012 } elseif( wpforo_is_audio( $extension ) ) {
1013 $f['value'] = sprintf( '<audio src="%1$s" controls title="%2$s"></audio>', esc_url( $file_url ), esc_attr( $file_name ) );
1014 } elseif( wpforo_is_video( $extension ) ) {
1015 $f['value'] = sprintf( '<video src="%1$s" controls title="%2$s"></video>', esc_url( $file_url ), esc_attr( $file_name ) );
1016 } else {
1017 $f['value'] = sprintf( '<a href="%s" target="_blank">%s</a>', esc_url( $file_url ), esc_html( $file_name ) );
1018 }
1019 }
1020 break;
1021 case 'avatar':
1022 $f['value'] = ( WPF()->usergroup->can( 'va' ) && wpforo_setting( 'profiles', 'avatars' ) ) ? WPF()->member->get_avatar_html( $f['value'], $userid ) : '';
1023 break;
1024 case 'color':
1025 if( $f['value'] ) {
1026 $f['value'] = '<input type="color" value="' . esc_attr( $f['value'] ) . '" disabled title="' . esc_attr( $f['value'] ) . '" style="min-width: 100px; min-height: 25px;">';
1027 }
1028 break;
1029 }
1030 switch( $f['name'] ) {
1031 case 'skype':
1032 $f['value'] = sprintf( '<a href="skype:%s?userinfo" rel="nofollow">%s</a>', esc_attr( $f['value'] ), esc_html( $f['value'] ) );
1033 break;
1034 case 'location':
1035 $f['value'] = sprintf( '<a href="//maps.google.com/?q=%s" target="_blank" rel="nofollow">%s</a>', esc_attr( $f['value'] ), esc_html( $f['value'] ) );
1036 break;
1037 case 'signature':
1038 $f['value'] = wpforo_signature( $f['value'], [ 'echo' => 0 ] );
1039 break;
1040 case 'about':
1041 $f['value'] = wpforo_nofollow_tag( $f['value'] );
1042 break;
1043 }
1044
1045 return apply_filters( 'wpforo_form_prepare_values', $f );
1046 }
1047
1048 /**
1049 * Prepares arguments
1050 *
1051 * @param array $f field arguments
1052 *
1053 * @return array prepared values
1054 */
1055 public function prepare_args( $f ) {
1056
1057 $is_owner = $this->owner();
1058
1059 //field_class
1060 $f['field_class'] = sanitize_text_field( $f['name'] );
1061
1062 //varname
1063 $f['varname'] = $f['isDefault'] ? (string) wpfval( $this->current, 'varname' ) : 'data';
1064
1065 //template
1066 $f['template'] = ( isset( $this->current['template'] ) ) ? $this->current['template'] : WPF()->current_object['template'];
1067
1068 //value
1069 $f['value'] = ( isset( $this->current['value'][ $f['name'] ] ) ) ? $this->current['value'][ $f['name'] ] : $f['value'];
1070 if( ! $f['isDefault'] && $f['varname'] ) {
1071 $f['value'] = ( isset( $this->current['value'][ $f['varname'] ][ $f['name'] ] ) ) ? $this->current['value'][ $f['varname'] ][ $f['name'] ] : $f['value'];
1072 }
1073 $f['value'] = wpforo_unslashe( $f['value'] );
1074 $f['value'] = wpforo_decode( $f['value'] );
1075
1076 if( $f['name'] === 'user_nicename' ) {
1077 $f['value'] = urldecode( (string) $f['value'] );
1078 }
1079
1080 //allowedGroupIds
1081 $groups = [];
1082 if( ! empty( $f['allowedGroupIds'] ) ) $groups = $this->build_array_value( $f['allowedGroupIds'] );
1083 if( $f['type'] === 'usergroup' ) {
1084 if( ! $is_owner && wpforo_current_user_is( 'admin' ) ) {
1085 $groups = WPF()->usergroup->get_usergroups( 'groupid' );
1086 } elseif( $is_owner && ! in_array( WPF()->current_user_groupid, $f['allowedGroupIds'] ) ) {
1087 $groups = [];
1088 }
1089 }
1090 $f['allowedGroupIds'] = array_filter( $groups );
1091
1092 //isRequired
1093 if( $f['isRequired'] ) {
1094 $f['required_class'] = ' wpf-field-required ';
1095 $f['required_indicator'] = ' <span class="wpf-field-required-icon" title="' . esc_attr( wpforo_phrase( 'Required field', false ) ) . '">*</span>';
1096 } else {
1097 $f['required_class'] = '';
1098 $f['required_indicator'] = '';
1099 }
1100
1101 return $f;
1102 }
1103
1104 /**
1105 * @param array $f field arguments
1106 *
1107 * @return array prepared values
1108 */
1109 public function prepare_field_args( $f ) {
1110 //faIcon
1111 $f['faIcon'] = trim( (string) $f['faIcon'] );
1112 if( strpos( $f['faIcon'], ' ' ) === false ) $f['faIcon'] = 'fas ' . $f['faIcon'];
1113
1114 //isRequired
1115 $f['isRequired'] = ( $f['isRequired'] ) ? ' required="required" ' : '';
1116
1117 //isDisabled
1118 $f['isDisabled'] = ( $f['isDisabled'] ) ? ' disabled="disabled" ' : '';
1119
1120 //fieldName | new key in args
1121 $f['fieldName'] = ( ! empty( $f['varname'] ) ? $f['varname'] . '[' . $f['name'] . ']' : $f['name'] );
1122
1123 //isMultiChoice
1124 if( $f['isMultiChoice'] ) {
1125 $f['fieldName'] .= '[]';
1126 $f['isMultiChoice'] = ' multiple="multiple" ';
1127 } else {
1128 $f['isMultiChoice'] = '';
1129 }
1130
1131 //fieldId | new key in args
1132 $f['fieldId'] = ( $f['varname'] ? $f['varname'] . '_' : '' ) . ( $f['id'] ?: $f['name'] );
1133 $f['fieldId'] = uniqid( $f['fieldId'] . '_' );
1134
1135 //minLength & maxLength
1136 $f['minLength'] = ( $f['minLength'] ) ? intval( $f['minLength'] ) : '';
1137 $f['maxLength'] = ( $f['maxLength'] ) ? intval( $f['maxLength'] ) : '';
1138 if( $f['minLength'] ) {
1139 $minLength_attr = ( $f['type'] === 'date' || $f['type'] === 'number' || $f['type'] === 'range' ) ? ' min="' . $f['minLength'] . '" ' : ' minlength="' . $f['minLength'] . '" ';
1140 }
1141 if( $f['maxLength'] ) {
1142 $maxLength_attr = ( $f['type'] === 'date' || $f['type'] === 'number' || $f['type'] === 'range' ) ? ' max="' . $f['maxLength'] . '" ' : ' maxlength="' . $f['maxLength'] . '" ';
1143 }
1144 $f['minmax'] = isset( $minLength_attr ) ? $minLength_attr : '';
1145 $f['minmax'] .= isset( $maxLength_attr ) ? ' ' . $maxLength_attr : '';
1146
1147 //attributes
1148 $f['attributes'] .= ' autocomplete="off"';
1149
1150 if( wpfkey( $f, 'fileExtensions' ) ) {
1151 if( is_scalar( $f['fileExtensions'] ) ) $f['fileExtensions'] = explode( ',', $f['fileExtensions'] );
1152 $f['fileExtensions'] = array_filter( $f['fileExtensions'] );
1153 }
1154
1155 return $f;
1156 }
1157
1158 /**
1159 * @param array $file_data
1160 * @param int $userid
1161 *
1162 * @return array
1163 */
1164 public function prepare_file_args( $file_data, $userid = 1 ) {
1165 $file = [ 'files' => [], 'fields' => [] ];
1166 $userid = $userid ?: WPF()->current_userid;
1167
1168 if( ! empty( $file_data ) ) {
1169 foreach( $file_data as $file_field_name => $file_name ) {
1170 $field_name_folder = substr( (string) $file_field_name, 6 );
1171 $file_upload_dir = wpforo_fix_dir_sep(
1172 WPF()->folders['wp_upload']['dir'] . $this->field_dir . $userid . DIRECTORY_SEPARATOR . $field_name_folder
1173 ) . DIRECTORY_SEPARATOR;
1174 $file_path = $file_upload_dir . $file_name;
1175 $file['files'][ $file_field_name ] = $file_path;
1176 $file['fields'][ $file_field_name ] = $this->field_dir . $userid . '/' . $field_name_folder . '/' . $file_name;
1177 wp_mkdir_p( $file_upload_dir );
1178 }
1179 }
1180
1181 return $file;
1182 }
1183
1184 public function check( $needle, $haystack ) {
1185 if( is_scalar( $haystack ) ) $haystack = explode( ',', $haystack );
1186
1187 return in_array( $needle, (array) $haystack ) ? 'checked' : '';
1188 }
1189
1190 public function select( $needle, $haystack ) {
1191 return in_array( $needle, (array) $haystack ) ? 'selected' : '';
1192 }
1193
1194 public function build_label( $item_label, $item_html, $f, $for = '' ) {
1195 $label = '<label for="' . $for . '" class="wpf-' . $f['type'] . '-label">' . stripslashes( (string) $item_label ) . '</label>';
1196 $field_html = $f['isLabelFirst'] ? $label . '&nbsp;' . $item_html : $item_html . '&nbsp;' . $label;
1197 if( $f['isWrapItem'] ) $field_html = '<label for="' . $for . '">' . $field_html . '</label>';
1198
1199 return $field_html;
1200 }
1201
1202 public function build_item_value_lable( $string, $sep = '=>' ) {
1203 $string = trim( (string) $string );
1204 $count = 0;
1205 $string = preg_replace( '#^\[(.+?)]$#isu', '$1', (string) $string, 1, $count );
1206 $default_selected = (bool) $count;
1207 $data = explode( $sep, $string );
1208 $value = wpfkey( $data, 0 ) ? trim( (string) $data[0] ) : 'no_value';
1209 $label = wpfkey( $data, 1 ) ? trim( (string) $data[1] ) : $value;
1210
1211 return compact( 'value', 'label', 'default_selected' );
1212 }
1213
1214 public function build_array_value( $var, $sep = ',' ) {
1215 if( is_scalar( $var ) ) {
1216 $var = trim( (string) $var );
1217 if( ! strlen( (string) $var ) ) return [];
1218 }
1219 if( is_serialized( $var ) ) {
1220 // This value can come straight from the request: the field state is
1221 // rebuilt from POST, and prepare_args() HTML-decodes it first, so an
1222 // entity-encoded payload arrives here fully restored. Never
1223 // instantiate classes, and never let one through as a value.
1224 $decoded = unserialize( $var, [ 'allowed_classes' => false ] );
1225 if( is_array( $decoded ) ) {
1226 $var = $decoded;
1227 } elseif( is_scalar( $decoded ) ) {
1228 $var = [ $decoded ];
1229 } else {
1230 return [];
1231 }
1232 } elseif( is_scalar( $var ) && strpos( (string) $var, $sep ) !== false ) {
1233 $var = explode( $sep, $var );
1234 }
1235
1236 // A multi-choice field value is a flat list of scalars. Only arrays and
1237 // objects are dropped, which a crafted payload can nest here and which
1238 // are a TypeError in trim() on PHP 8. Every other type is cast exactly
1239 // as trim() used to coerce it, so null and bool values are unchanged.
1240 $values = [];
1241 foreach( (array) $var as $key => $value ) {
1242 if( is_array( $value ) || is_object( $value ) ) continue;
1243 $values[ $key ] = trim( (string) $value );
1244 }
1245
1246 return $values;
1247 }
1248
1249 public function build_array_using_string_rows( $string, $regexp = '' ) {
1250 if( is_scalar( $string ) ) {
1251 if( ! $regexp ) $regexp = '#' . preg_quote( PHP_EOL ) . '#isu';
1252
1253 return array_filter( preg_split( $regexp, $string ) );
1254 }
1255
1256 return $string;
1257 }
1258
1259 public function sanitize( $data, $fields ) {
1260 $types = $this->field_types( $fields );
1261 if( ! empty( $data ) && ! empty( $types ) ) {
1262 foreach( $data as $name => $value ) {
1263 if( wpfval( $types, $name ) ) {
1264 $data[ $name ] = $this->sanitize_field( $value, $types[ $name ], $name );
1265 }
1266 }
1267 }
1268
1269 return $data;
1270 }
1271
1272 public function sanitize_field( $value, $type = 'text', $name = '' ) {
1273 if( ! is_null( $value ) ) {
1274 if( $type === 'text' ) {
1275 $value = sanitize_text_field( $value );
1276 if( $name === 'user_nicename' ) {
1277 $value = sanitize_title( sanitize_user( $value, true ) );
1278 }
1279 } elseif( $type === 'url' ) {
1280 $value = esc_url_raw( $value );
1281 } elseif( $type === 'date' ) {
1282 $value = sanitize_text_field( $value );
1283 } elseif( $type === 'textarea' ) {
1284 $value = stripslashes( wpforo_kses( trim( (string) $value ), 'user_description' ) );
1285 } elseif( $type === 'email' ) {
1286 $value = sanitize_email( $value );
1287 } elseif( $type === 'password' ) {
1288 $value = trim( (string) $value );
1289 } elseif( $type === 'usergroup' ) {
1290 $value = intval( $value );
1291 } elseif( $type === 'radio' ) {
1292 $value = sanitize_text_field( $value );
1293 } elseif( $type === 'checkbox' ) {
1294 if( $name === 'secondary_groupids' ) {
1295 $value = wpforo_sanitize_int( $value );
1296 } else {
1297 $value = wpforo_sanitize_text( $value );
1298 }
1299 } elseif( $type === 'select' ) {
1300 $value = sanitize_text_field( $value );
1301 } elseif( $type === 'color' ) {
1302 $value = sanitize_text_field( $value );
1303 } elseif( $type === 'number' ) {
1304 $value = intval( $value );
1305 } elseif( $type === 'tel' ) {
1306 $value = sanitize_text_field( $value );
1307 } elseif( $type === 'html' ) {
1308 $value = preg_replace( '#<script(.*?)>(.*?)</script>#is', '', (string) $value );
1309 }
1310
1311 if( is_string( $value ) ) {
1312 $value = stripslashes( (string) $value );
1313 }
1314 }
1315
1316 return $value;
1317 }
1318
1319 /**
1320 * Whether prepare_values()/esc_field() already turned $f['value'] into trusted,
1321 * pre-escaped HTML for this field type/name (so it must NOT be esc_html()'d again
1322 * before being echoed).
1323 *
1324 * @param array $f field arguments (after prepare_values()/esc_field())
1325 *
1326 * @return bool
1327 */
1328 public function is_display_value_safe_html( $f ) {
1329 $safe_types = [ 'url', 'email', 'tel', 'file', 'avatar', 'color', 'textarea', 'tinymce', 'datetime', 'html' ];
1330 $safe_names = [ 'skype', 'location', 'signature', 'about' ];
1331
1332 return in_array( wpfval( $f, 'type' ), $safe_types, true ) || in_array( wpfval( $f, 'name' ), $safe_names, true );
1333 }
1334
1335 public function esc_field( $f ) {
1336 if( wpfkey( $f, 'value' ) ) {
1337 $f['value'] = wpforo_trim( $f['value'] );
1338 if( in_array( wpfval( $f, 'type' ), [ 'textarea', 'tinymce' ], true ) ) {
1339 $f['value'] = wpautop( wpforo_kses( stripslashes( (string) $f['value'] ) ) );
1340 } elseif( wpfval( $f, 'type' ) === 'file' ) {
1341 $f['value'] = wpfval( $f['value'], 'fileurl' ) ?: $f['value'];
1342 } elseif( $f['name'] === 'timezone' ) {
1343 $f['value'] = str_replace( '_', ' ', $f['value'] );
1344 } elseif( $f['fieldKey'] === 'secondary_groupids' ) {
1345 $f['value'] = WPF()->usergroup->get_secondary_group_names( $f['value'] );
1346 $f['value'] = implode( ', ', $f['value'] );
1347 } elseif( $f['type'] === 'usergroup' && $f['fieldKey'] === 'groupid' && ( $f['value'] = intval( $f['value'] ) ) ) {
1348 if( $group = WPF()->usergroup->get_usergroup( $f['value'] ) ) $f['value'] = wpfval( $group, 'name' );
1349 } elseif( is_array( $f['value'] ) ) {
1350 $f['value'] = array_filter( $f['value'] );
1351 $f['value'] = implode( ', ', $f['value'] );
1352 }
1353 }
1354 $f['value'] = apply_filters( 'wpforo_display_field_value', $f['value'], $f );
1355
1356 return $f;
1357 }
1358
1359 public function validate( &$data, &$fields ) {
1360 $type = '';
1361 $label = '';
1362 $userid = '';
1363 $error = [];
1364 $return = [];
1365 $is_owner = $this->owner();
1366 $template = ( isset( $this->current['template'] ) ) ? $this->current['template'] : WPF()->current_object['template'];
1367 if( empty( $data ) ) $error[] = 'No data submitted';
1368 if( empty( $fields ) ) $error[] = 'User profile fields not found';
1369 if( empty( $error ) ) {
1370 foreach( $fields as $r_key => $rows ) {
1371 foreach( $rows as $c_key => $cols ) {
1372 foreach( $cols as $key => $field ) {
1373 if( wpfval( $field, 'name' ) ) {
1374 $name = $field['name'];
1375 if( ! $template && wpfval( $field, 'template' ) ) $template = $field['template'];
1376 if( wpfval( $field, 'label' ) ) $label = esc_html( $field['label'] );
1377 if( wpfval( $field, 'type' ) ) $type = $field['type'];
1378 if( wpfval( $data, 'userid' ) ) $userid = $data['userid'];
1379 if( $template && $template !== 'register' ) {
1380 if( $template === 'account' && $field['type'] === 'password' ) $field['isRequired'] = 0;
1381 if( ! $this->can_edit( $field ) ) {
1382 unset( $cols[ $key ] );
1383 unset( $data[ $name ] );
1384 unset( $fields[ $r_key ][ $c_key ][ $key ] );
1385 }
1386 }
1387 if( wpfkey( $cols, $key ) && wpfkey( $data, $name ) ) {
1388 $value = $data[ $name ];
1389 if( is_string( $value ) ) {
1390 $value = trim( $value );
1391 $value = htmlspecialchars_decode( $value );
1392 }
1393 if( wpfval( $field, 'isRequired' ) && ! $value ) {
1394 $error[] = $label . ' ' . wpforo_phrase( 'field is required', false, false );
1395 }
1396 if( $value ) {
1397 if( $type === 'number' ) {
1398 if( wpfval( $field, 'minLength' ) ) {
1399 if( (int) $value < $field['minLength'] ) {
1400 $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field value must be at least %d', false, false ), intval( $field['minLength'] ) );
1401 }
1402 }
1403 if( wpfval( $field, 'maxLength' ) ) {
1404 if( (int) $value > $field['maxLength'] ) {
1405 $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field value cannot be greater than %d', false, false ), intval( $field['maxLength'] ) );
1406 }
1407 }
1408 } else {
1409 if( wpfval( $field, 'minLength' ) ) {
1410 if( wpforo_strlen( $value ) < $field['minLength'] ) {
1411 $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field length must be at least %d characters', false, false ), intval( $field['minLength'] ) );
1412 }
1413 }
1414 if( wpfval( $field, 'maxLength' ) ) {
1415 if( wpforo_strlen( $value ) > $field['maxLength'] ) {
1416 $error[] = $label . ' ' . sprintf( wpforo_phrase( 'field length cannot be greater than %d characters', false, false ), intval( $field['maxLength'] ) );
1417 }
1418 }
1419 }
1420 if( $type === 'url' && filter_var( $value, FILTER_VALIDATE_URL ) === false ) {
1421 $error[] = $label . ' ' . wpforo_phrase( 'field value is not a valid URL', false, false );
1422 }
1423 if( $type === 'email' ) {
1424 if( ! is_email( $value ) ) {
1425 $error[] = $label . ' ' . wpforo_phrase( 'Invalid Email address', false, false );
1426 }
1427 if( $name === 'user_email' ) {
1428 $email_owner = email_exists( $value );
1429 if( $email_owner && $email_owner != $userid ) {
1430 $error[] = $label . ' ' . wpforo_phrase( 'This email address is already registered. Please insert another', false, false );
1431 }
1432 }
1433 }
1434 if( $type === 'file' ) {
1435 $extension = pathinfo( $value, PATHINFO_EXTENSION );
1436 $extension = ( function_exists( 'mb_strtolower' ) ) ? mb_strtolower( (string) $extension ) : strtolower( (string) $extension );
1437 if( wpfval( $field, 'fileExtensions' ) ) {
1438 if( $extension ) {
1439 if( ! in_array( $extension, $field['fileExtensions'] ) ) {
1440 $error[] = $label . ' ' . wpforo_phrase( 'file type is not allowed', false, false );
1441 $error[] = sprintf( 'Allowed file types: %s', implode( ', ', $field['fileExtensions'] ) );
1442 }
1443 } else {
1444 $error[] = $label . ' ' . wpforo_phrase( 'file type is not detected', false, false );
1445 }
1446 } else {
1447 $mime_types = get_allowed_mime_types();
1448 $mime_types = array_flip( $mime_types );
1449 if( ! empty( $mime_types ) ) {
1450 $implode_types = implode( '|', $mime_types );
1451 $explode_types = explode( '|', $implode_types );
1452 if( ! in_array( $extension, $explode_types ) ) {
1453 $error[] = $label . ' ' . sprintf( wpforo_phrase( 'file type %s is not allowed', false, false ), $extension );
1454 }
1455 if( ! WPF()->perm->can_attach_file_type( $extension ) ) {
1456 $error[] = 'You are not allowed to attach this file type';
1457 }
1458 }
1459 }
1460 if( wpfval( $field, 'fileSize' ) ) {
1461 if( wpfval( $_FILES, 'data', 'size', $name ) && $_FILES['data']['size'][ $name ] > ( $field['fileSize'] * 1024 * 1024 ) ) {
1462 $error[] = $label . ' ' . wpforo_phrase( 'file is too large', false, false );
1463 $error[] = sprintf( 'Maximum allowed file size is %s MB', $field['fileSize'] );
1464 }
1465 }
1466 }
1467 if( $name === 'user_nicename' ) {
1468 $value = sanitize_text_field( $value );
1469 $user_nicename = sanitize_title( sanitize_user( $value, true ) );
1470 if( ! $user_nicename ) {
1471 $error[] = 'Nickname validation failed';
1472 }
1473 $sql = "SELECT `ID` FROM `" . WPF()->db->users . "` WHERE `ID` != " . intval( $userid ) . " AND `user_nicename` LIKE '" . esc_sql( $user_nicename ) . "'";
1474 if( WPF()->db->get_var( $sql ) ) {
1475 $error[] = 'This nickname is already in use. Please insert another.';
1476 }
1477 }
1478 if( $name === 'groupid' ) {
1479 if( $template !== 'register' ) {
1480 if( $is_owner || ! wpforo_current_user_is( 'admin' ) ) {
1481 $error[] = 'You have no permission to edit Usergroup field';
1482 }
1483 } else {
1484 if( in_array( $value, [ 1, 2, 4 ] ) ) {
1485 $error[] = 'Admin and Moderator Usergroups are not permitted';
1486 } else {
1487 if( wpfval( $field, 'allowedGroupIds' ) ) {
1488 $allowedGroupIds = wpforo_parse_args( $field['allowedGroupIds'] );
1489 if( ! in_array( $value, $allowedGroupIds ) ) {
1490 $error[] = 'The selected Usergroup cannot be set';
1491 }
1492 } else {
1493 $error[] = 'The selected Usergroup is not found in allowed list';
1494 }
1495 }
1496 }
1497 }
1498 if( $name === 'secondary_groupids' ) {
1499 if( $template === 'register' || ( $is_owner && $field['isEditable'] ) || ( ! $is_owner && in_array(
1500 WPF()->current_user_groupid,
1501 (array) $field['canEdit']
1502 ) ) || wpforo_current_user_is( 'admin' ) ) {
1503 if( ! empty( $value ) && is_array( $value ) ) {
1504 $secondary_groupids = WPF()->usergroup->get_secondary_groupids();
1505 foreach( $value as $secondary_groupid ) {
1506 if( in_array( $secondary_groupid, [ 1, 2, 4 ] ) ) {
1507 $error[] = 'Admin and Moderator Usergroups are not permitted';
1508 }
1509 if( $secondary_groupid && ! in_array( $secondary_groupid, $secondary_groupids ) ) {
1510 $error[] = 'One of the selected Usergroups cannot be set as Secondary';
1511 }
1512 }
1513 }
1514 } else {
1515 $error[] = 'You have no permission to edit Usergroup field';
1516 }
1517 }
1518 }
1519 }
1520 }
1521 }
1522 }
1523 }
1524 }
1525 if( ! empty( $error ) ) {
1526 $return['error'] = $error;
1527
1528 return $return;
1529 } else {
1530 return true;
1531 }
1532 }
1533
1534 public function validate_password( $data ) {
1535 $error = [];
1536 $return = [ 'error' => false ];
1537 if( wpfval( $data, 'old_pass' ) && wpfval( $data, 'user_pass1' ) && wpfval( $data, 'user_pass2' ) ) {
1538 if( $data['user_pass1'] !== $data['user_pass2'] ) {
1539 $error[] = 'New Passwords do not match';
1540 } else {
1541 return true;
1542 }
1543 }
1544 if( ! empty( $error ) ) {
1545 $return['error'] = $error;
1546
1547 return $return;
1548 }
1549
1550 return false;
1551 }
1552
1553 public function field_types( $fields ) {
1554 $name_type_fields = [];
1555 if( ! empty( $fields ) ) {
1556 foreach( $fields as $rows ) {
1557 foreach( $rows as $cols ) {
1558 foreach( $cols as $field ) {
1559 if( wpfval( $field, 'name' ) && wpfval( $field, 'type' ) ) {
1560 $name_type_fields[ $field['name'] ] = $field['type'];
1561 }
1562 }
1563 }
1564 }
1565 }
1566
1567 return $name_type_fields;
1568 }
1569
1570 public function owner( $object_userid = false ): bool {
1571 if( ! $object_userid ) {
1572 if( wpfval( WPF()->current_object, 'user', 'userid' ) ) {
1573 return wpforo_is_owner( WPF()->current_object['user']['userid'] );
1574 } else {
1575 return false;
1576 }
1577 } else {
1578 return wpforo_is_owner( $object_userid );
1579 }
1580 }
1581
1582 /**
1583 * @param array $f
1584 *
1585 * @return bool
1586 */
1587 public function can_add( $f ) {
1588 if( wpfval( $f, 'name' ) ) {
1589 if( $f['name'] === 'signature' && ! wpforo_setting( 'profiles', 'signature' ) ) {
1590 return false;
1591 }
1592 if( $f['name'] === 'avatar' && ( ! wpforo_setting( 'profiles', 'custom_avatars' ) || ! wpforo_setting( 'profiles', 'avatars' ) ) ) {
1593 return false;
1594 }
1595 }
1596
1597 return true;
1598 }
1599
1600 /**
1601 * @param array $f
1602 *
1603 * @return bool
1604 */
1605 public function can_view( $f ) {
1606 return wpforo_current_user_is( 'admin' ) || ! ( ! $this->owner() && ! in_array( WPF()->current_user_groupid, (array) $f['canView'] ) );
1607 }
1608
1609 /**
1610 * @param array $f
1611 *
1612 * @return bool
1613 */
1614 public function can_edit( $f ) {
1615 if( wpfval( WPF()->current_object, 'user', 'userid' ) ) {
1616 $is_owner = $this->owner();
1617 $value = wpfkey( $f, 'value' ) ? $f['value'] : false;
1618 $can_edit = wpfkey( $f, 'isEditable' ) ? $f['isEditable'] : false;
1619 $can_moderate = $this->can_moderate( $f );
1620 if( ! $is_owner && ! $can_moderate && WPF()->current_user_groupid !== 1 ) {
1621 return false;
1622 }
1623 if( ! $can_edit && ! $can_moderate && WPF()->current_user_groupid !== 1 && ! $value ) {
1624 return false;
1625 }
1626 if( wpfkey( $f, 'name' ) ) {
1627 if( $f['name'] === 'signature' && ( ! WPF()->usergroup->can( 'ups' ) || ! wpforo_setting( 'profiles', 'signature' ) ) ) {
1628 return false;
1629 }
1630 if( $f['name'] === 'avatar' && ( ! wpforo_setting( 'profiles', 'custom_avatars' ) || ! wpforo_setting( 'profiles', 'avatars' ) ) ) {
1631 return false;
1632 }
1633 if( $f['name'] === 'groupid' && ( WPF()->current_user_groupid != 1 || $is_owner || ! current_user_can( 'administrator' ) ) ) {
1634 return false;
1635 }
1636 }
1637
1638 return true;
1639 } else {
1640 return false;
1641 }
1642 }
1643
1644 /**
1645 * @param array $f
1646 *
1647 * @return bool
1648 */
1649 public function can_moderate( $f ) {
1650 if( empty( $f ) ) return false;
1651 $usergroups_who_can_edit = ! empty( $f['canEdit'] ) ? (array) $f['canEdit'] : [ 1 ];
1652
1653 return in_array( WPF()->current_user_groupid, $usergroups_who_can_edit );
1654 }
1655
1656 public function can_show_value( $f ) {
1657 $return = true;
1658 if( is_string( $f ) ) $f = WPF()->member->get_field( $f );
1659 $f = wpforo_parse_args( $f, $this->default );
1660 if( $f['type'] === 'password' ) $return = false;
1661
1662 return apply_filters( 'wpforo_form_can_show_value', $return, $f );
1663 }
1664 }
1665