diff --git a/adminpages/member-edit.php b/adminpages/member-edit.php index 00f27ca5c..74e996cce 100644 --- a/adminpages/member-edit.php +++ b/adminpages/member-edit.php @@ -25,11 +25,19 @@ function pmpro_member_edit_get_panels() { // Add user fields panels. $user_id = PMPro_Member_Edit_Panel::get_user()->ID; if ( $user_id ) { - $profile_user_fields = pmpro_get_user_fields_for_profile( $user_id, true ); - if ( ! empty( $profile_user_fields ) ) { - foreach ( $profile_user_fields as $group_name => $user_fields ) { - $panels[] = new PMPro_Member_Edit_Panel_User_Fields( $group_name ); + foreach( PMPro_Field_Group::get_all() as $group ) { + $fields_to_display = $group->get_fields_to_display( + array( + 'scope' => 'profile', + 'user_id' => $user_id, + ) + ); + + if ( empty( $fields_to_display ) ) { + continue; } + + $panels[] = new PMPro_Member_Edit_Panel_User_Fields( $group->name ); } } diff --git a/adminpages/member-edit/pmpro-class-member-edit-panel-user-fields.php b/adminpages/member-edit/pmpro-class-member-edit-panel-user-fields.php index 30d0aff86..4155d4e8d 100644 --- a/adminpages/member-edit/pmpro-class-member-edit-panel-user-fields.php +++ b/adminpages/member-edit/pmpro-class-member-edit-panel-user-fields.php @@ -1,12 +1,19 @@ field_group = PMPro_Field_Group::get( $field_group_name ); $this->slug = 'user-fields-' . sanitize_title( $field_group_name ); - $this->title = $field_group_name; + $this->title = $this->field_group->label; $this->submit_text = current_user_can( 'edit_users' ) ? __( 'Update Member', 'paid-memberships-pro' ) : ''; } @@ -15,9 +22,8 @@ public function __construct( $field_group_name ) { */ protected function display_panel_contents() { // Print the group description. - $field_group = pmpro_get_field_group_by_name( $this->title ); - if ( ! empty( $field_group->description ) ) { - echo wp_kses_post( $field_group->description ); + if ( ! empty( $this->field_group->description ) ) { + echo wp_kses_post( $this->field_group->description ); } // Check if this is a checkout field location and show a message about custom code. @@ -31,21 +37,22 @@ protected function display_panel_contents() { 'before_submit_button', 'just_profile' ); - if ( in_array( $this->title, $checkout_field_locations ) ) { + if ( in_array( $this->field_group->name, $checkout_field_locations ) ) { esc_html_e( 'These user fields were added via custom code to hook into the following location:', 'paid-memberships-pro' ); - echo ' ' . esc_html( $this->title ) . ''; + echo ' ' . esc_html( $this->field_group->name ) . ''; } // Print the fields. - $profile_user_fields = pmpro_get_user_fields_for_profile( self::get_user()->ID, true ); ?> title] as $field ) { - if ( pmpro_is_field( $field ) ) { - $field->displayInProfile( self::get_user()->ID ); // Field will be readonly if cannot edit users. - } - } + $this->field_group->display( + array( + 'markup' => 'table', + 'show_group_label' => false, + 'user_id' => self::get_user()->ID, + ) + ); ?>
ID ) !== false ); // Function returns false on failed, null on saved. Will check edit_users cap in function. + $saved = $this->field_group->save_fields( + array( + 'user_id' => self::get_user()->ID, + ) + ); // Show success message. if ( $saved ) { diff --git a/adminpages/user-fields/delete-field.php b/adminpages/user-fields/delete-field.php new file mode 100644 index 000000000..684443186 --- /dev/null +++ b/adminpages/user-fields/delete-field.php @@ -0,0 +1,43 @@ +fields as $field_setting ) { + if ( $field_setting->name === $delete_name ) { + $deleted = true; + } else { + $new_fields[] = $field_setting; + } + } + $group_setting->fields = $new_fields; + $new_settings[] = $group_setting; + } + + if ( $deleted ) { + // Save the new settings. + update_option( 'pmpro_user_fields_settings', $new_settings ); + + // Show a success message. + pmpro_setMessage( __( 'Field deleted.', 'paid-memberships-pro' ), 'success' ); + + // Redirect with javascript. + ?> + + name === $delete_name ) { + $deleted = true; + } else { + $new_settings[] = $group_setting; + } + } + + if ( $deleted ) { + // Save the new settings. + update_option( 'pmpro_user_fields_settings', $new_settings ); + + // Show a success message. + pmpro_setMessage( __( 'Group deleted.', 'paid-memberships-pro' ), 'success' ); + + // Redirect with javascript. + ?> + + fields as $f ) { + if ( $f->name === $edit ) { + $field = $f; + $field->group = $group->name; + break 2; + } + } + } +} + +// If we still don't have a field, get default settings. +if ( empty( $field ) ) { + $field = new stdClass(); + $field->name = ''; + $field->label = ''; + $field->type = ''; + $field->required = false; + $field->readonly = false; + $field->profile = ''; + $field->wrapper_class = ''; + $field->element_class = ''; + $field->hint = ''; + $field->default = ''; + $field->options = ''; + $field->allowed_file_types = ''; + $field->max_file_size = ''; + $field->group = empty( $_REQUEST['group'] ) ? '' : sanitize_text_field( $_REQUEST['group'] ); +} + +?> +
+name ) ) { ?> +

+ name ) + ); + ?> +

+ +

+ + + +
+

+
+ +
+ + + +
+
+ +
+
+ + + + + + + name ) ) { + ?> + + + + + + + + + + + + +
+ +

+
+ +

+
+ +

+
+
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +

+
+ +

+
+ +

+
+ + +

+
+
+
+ +
+
+ +
+
+ + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + +
+ +
+ +

+
+ +

+
+
+
+ +

+ + +

+
\ No newline at end of file diff --git a/adminpages/user-fields/edit-group.php b/adminpages/user-fields/edit-group.php new file mode 100644 index 000000000..c3d14c34c --- /dev/null +++ b/adminpages/user-fields/edit-group.php @@ -0,0 +1,159 @@ +name === $edit_group ) { + $group = $group_settings; + break; + } + } +} + +// If we still don't have a group, get default settings. +if ( empty( $group ) ) { + $group = new stdClass(); + $group->name = ''; + $group->label = ''; + $group->checkout = 'yes'; + $group->profile = 'yes'; + $group->description = ''; + $group->levels = array(); +} + +// If the name is set but not the label, set the label to the name. +if ( ! empty( $group->name ) && empty( $group->label ) ) { + $group->label = $group->name; +} + +// Get all membership levels. +$levels = pmpro_sort_levels_by_order( pmpro_getAllLevels( true, true ) ); + +?> +
+name ) ) { ?> +

+ name ) + ); + ?> +

+ +

+ + + +
+

+
+ +
+ + + +
+
+ +
+
+ + + + + + + + + + + + + + + +
+ +

+
+ + +

+
+ +

+
+
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + +
+ +
+ +
+
3 ) { ?>style="height: 90px; overflow: auto;"> + +
+ +
+ +
+
+
+
+ +

+ + +

+
\ No newline at end of file diff --git a/adminpages/user-fields/save-field.php b/adminpages/user-fields/save-field.php new file mode 100644 index 000000000..e08d0eb89 --- /dev/null +++ b/adminpages/user-fields/save-field.php @@ -0,0 +1,107 @@ +name = pmpro_getParam( 'name', 'POST' ); + $field->label = pmpro_getParam( 'label', 'POST' ); + $field->type = pmpro_getParam( 'type', 'POST' ); + $field->required = pmpro_getParam( 'required', 'POST' ); + $field->readonly = pmpro_getParam( 'readonly', 'POST' ); + $field->profile = pmpro_getParam( 'profile', 'POST' ); + $field->wrapper_class = pmpro_getParam( 'wrapper_class', 'POST' ); + $field->element_class = pmpro_getParam( 'element_class', 'POST' ); + $field->hint = pmpro_getParam( 'hint', 'POST', '', 'wp_kses_post' ); + $field->options = pmpro_getParam( 'options', 'POST', '', 'sanitize_textarea_field' ); + $field->allowed_file_types = pmpro_getParam( 'allowed_file_types', 'POST' ); + $field->max_file_size = pmpro_getParam( 'max_file_size', 'POST' ); + $field->default = pmpro_getParam( 'default', 'POST' ); + + if ( empty( $field->type ) ) { + pmpro_setMessage( __( 'Type is required.', 'paid-memberships-pro' ), -1 ); + } +} + +// Make sure that there was a group passed. +if ( -1 !== $pmpro_msgt ) { + $group = pmpro_getParam( 'group', 'POST' ); + if ( empty( $group ) ) { + pmpro_setMessage( __( 'Group is required.', 'paid-memberships-pro' ), -1 ); + } +} + +// Make sure that the group is valid. +if ( -1 !== $pmpro_msgt ) { + $current_settings = pmpro_get_user_fields_settings(); + $group_obj = null; + foreach ( $current_settings as $group_setting ) { + if ( $group_setting->name === $group ) { + $group_obj = $group_setting; + break; + } + } + if ( empty( $group_obj ) ) { + pmpro_setMessage( __( 'Invalid group.', 'paid-memberships-pro' ), -1 ); + } +} + +// If there are no errors, save the field. +if ( -1 !== $pmpro_msgt ) { + // If the field is already in the group, update it. + $found = false; + $new_fields = array(); + foreach ( $group_obj->fields as $key => $field_setting ) { + if ( $field_setting->name === $field->name ) { + $new_fields[] = $field; + $found = true; + } else { + $new_fields[] = $field_setting; + } + } + + // If the field was not found, add it. + if ( ! $found ) { + $new_fields[] = $field; + } + $group_obj->fields = $new_fields; + + // Delete fields with this name from other groups and update the current group. + $new_settings = array(); + foreach ( $current_settings as $group_setting ) { + if ( $group_setting->name === $group ) { + $new_settings[] = $group_obj; + } else { + $new_fields = array(); + foreach ( $group_setting->fields as $field_setting ) { + if ( $field_setting->name !== $field->name ) { + $new_fields[] = $field_setting; + } + } + $group_setting->fields = $new_fields; + $new_settings[] = $group_setting; + } + } + + // Save the new settings. + update_option( 'pmpro_user_fields_settings', $new_settings ); + + // Set the field being edited. + $_REQUEST['edit'] = $field->name; + + // Show a success message. + pmpro_setMessage( __( 'Field saved.', 'paid-memberships-pro' ) . ' ' . __( 'View All Fields.', 'paid-memberships-pro' ) . '', 'pmpro_success' ); + + // Redirect with javascript. + ?> + + name = pmpro_getParam( 'name', 'POST' ); + $group->label = pmpro_getParam( 'label', 'POST' ); + $group->checkout = pmpro_getParam( 'checkout', 'POST' ); + $group->profile = pmpro_getParam( 'profile', 'POST' ); + $group->description = pmpro_getParam( 'description', 'POST', '', 'wp_kses_post' ); + $group->levels = array_map( 'intval', empty( $_POST['levels'] ) ? array() : $_POST['levels'] ); +} + +// If the name has changed but there is already another group with that name, show an error. +if ( -1 !== $pmpro_msgt && $group->name !== $_REQUEST['original_name'] ) { + $all_groups = PMPro_Field_Group::get_all(); + if ( isset( $all_groups[ $group->name ] ) ) { + pmpro_setMessage( __( 'A group with that name already exists.', 'paid-memberships-pro' ), -1 ); + } +} + +// If the group is hidden from both the checkout and profile pages, show an error. +// This will prevent any fields from being loaded in pmpro_load_user_fields_from_settings(). +if ( -1 !== $pmpro_msgt && 'no' === $group->checkout && 'no' === $group->profile ) { + pmpro_setMessage( __( 'Group cannot be hidden from both the checkout and profile pages.', 'paid-memberships-pro' ), -1 ); +} + +// If there are no errors, save the group. +if ( -1 !== $pmpro_msgt ) { + $current_settings = pmpro_get_user_fields_settings(); + $new_settings = array(); + $added = false; + foreach ( $current_settings as $group_setting ) { + if ( $group_setting->name === $_REQUEST['original_name'] ) { + $group->fields = $group_setting->fields; + $new_settings[] = $group; + $added = true; + } else { + $new_settings[] = $group_setting; + } + } + + // If the group is new, add it to the settings. + if ( ! $added ) { + $new_settings[] = $group; + } + + // Save the new settings. + update_option( 'pmpro_user_fields_settings', $new_settings ); + + // Set the field being edited. + $_REQUEST['edit_group'] = $group->name; + + // Show a success message. + pmpro_setMessage( __( 'Group saved.', 'paid-memberships-pro' ) . ' ' . __( 'View All Fields.', 'paid-memberships-pro' ) . '', 'pmpro_success' ); + + // Redirect with javascript. + ?> + + name = sanitize_text_field( $group->name ); - $group->checkout = 'yes' === $group->checkout ? 'yes' : 'no'; - $group->profile = sanitize_text_field( $group->profile ); - $group->description = wp_kses_post( $group->description ); - $group->levels = array_map( 'intval', $group->levels ); - foreach ( $group->fields as $field ) { - $field_name = pmpro_format_field_name( $field->name ); //Replace spaces and dashes with underscores. - $field->name = sanitize_text_field( $field_name ); - $field->label = wp_kses_post( $field->label ); - $field->type = sanitize_text_field( $field->type ); - $field->required = 'yes' === $field->required ? 'yes' : 'no'; - $field->readonly = 'yes' === $field->readonly ? 'yes' : 'no'; - $field->profile = sanitize_text_field( $field->profile ); - $field->wrapper_class = sanitize_text_field( $field->wrapper_class ); - $field->element_class = sanitize_text_field( $field->element_class ); - $field->hint = wp_kses_post( $field->hint ); - $field->options = sanitize_textarea_field( $field->options ); + // Show header. + require_once(dirname(__FILE__) . "/admin_header.php"); + + // Show page contents. + if ( isset( $_REQUEST['edit'] ) ) { + // Editing a field. + $edit = sanitize_text_field( $_REQUEST['edit'] ); + require_once( PMPRO_DIR . '/adminpages/user-fields/edit-field.php' ); + } elseif ( isset( $_REQUEST['edit_group'] ) ) { + // Editing a group. + $edit_group = sanitize_text_field( $_REQUEST['edit_group'] ); + require_once( PMPRO_DIR . '/adminpages/user-fields/edit-group.php' ); + } else { + // Showing the fields list. + $groups = PMPro_Field_Group::get_all(); + + // Order the groups based on how they would show at checkout. + $ordered_groups = array(); + $pre_checkout_field_locations = array( + 'after_username', + 'after_password', + 'after_pricing_fields', + 'after_email', + ); + $post_checkout_field_locations = array( + 'after_billing_fields', + 'after_captcha', + 'before_submit_button', + 'after_tos_fields', + ); + + // Add all of the "pre-checkout" field groups to the ordered groups array. + foreach ( $pre_checkout_field_locations as $location ) { + if ( array_key_exists( $location, $groups ) ) { + $ordered_groups[] = $groups[ $location ]; } } - update_option( 'pmpro_user_fields_settings', $groups, false ); - - // Assume success. - $msg = true; - $msgt = __( 'Your user field settings have been updated.', 'paid-memberships-pro' ); - } + // Add all of the "checkout" field groups to the ordered groups array. + foreach ( $groups as $group ) { + if ( ! in_array( $group->name, $pre_checkout_field_locations ) && ! in_array( $group->name, $post_checkout_field_locations ) ) { + $ordered_groups[] = $group; + } + } - /** - * Get the user fields from options. - */ - $user_fields_settings = pmpro_get_user_fields_settings(); + // Add all of the "post-checkout" field groups to the ordered groups array. + foreach ( $post_checkout_field_locations as $location ) { + if ( array_key_exists( $location, $groups ) ) { + $ordered_groups[] = $groups[ $location ]; + } + } - /** - * Load the common header for admin pages. - * - */ - require_once( dirname(__FILE__) . '/admin_header.php' ); + // Get lists of all groups and fields that were created via UI. + $ui_settings = pmpro_get_user_fields_settings(); + $ui_groups = array(); + $ui_fields = array(); + if ( is_array( $ui_settings ) ) { + foreach ( $ui_settings as $group_setting ) { + $ui_groups[] = $group_setting->name; + foreach ( $group_setting->fields as $field ) { + $ui_fields[] = $field->name; + } + } + } - // Show warning if there are additional fields that are coded. - if ( pmpro_has_coded_user_fields() ) { ?> -
-

-
- + jQuery(document).ready(function($) { - /** - * Meta boxes for User Fields admin page. - * - */ - add_meta_box( - 'pmpro_userfields_save', - esc_html__( 'Save', 'paid-memberships-pro' ), - 'pmpro_userfields_save_widget', - 'memberships_page_pmpro-userfields', - 'side' - ); - add_meta_box( - 'pmpro_userfields_help', - esc_html__( 'User Fields Help', 'paid-memberships-pro' ), - 'pmpro_userfields_help_widget', - 'memberships_page_pmpro-userfields', - 'side' - ); - - /** - * Meta box to show a save button and other data. - * - */ - function pmpro_userfields_save_widget() { ?> -

- -

- ' + }; - /** - * Meta box to show help information. - * - */ - function pmpro_userfields_help_widget() { ?> -

-

-

+ $.post(ajaxurl, data, function(response) { + }); + } + + $('.pmpro_section-sort-button-move-up').on('click',function(){ + var current = $(this).closest('.pmpro_section'); + // Check if the group before this one is sortable. + if ( current.prev().hasClass('pmpro_section_sortable') ) { + current.prev().before(current); + update_level_group_order(); + } + }); + $('.pmpro_section-sort-button-move-down').on('click',function(){ + var current = $(this).closest('.pmpro_section'); + // Check if the group after this one is sortable. + if ( current.next().hasClass('pmpro_section_sortable') ) { + current.next().after(current); + update_level_group_order(); + } + }); + + function update_level_group_order(event, ui) { + group_order = []; + $(".pmpro_section_sortable").each(function() { + group_order.push( $(this).find('.pmpro-userfields-settings-group-name').val()); + }); + console.log(group_order); + + data = { + action: 'pmpro_update_field_group_order', + ordered_groups: group_order, + nonce: '' + }; + + $.post(ajaxurl, data, function(response) { + }); + } + }); + -
-

-
-
+ // Check if there are multiple fields with the same name. If so, show an error. + // TODO -
-
+ ?> +
+ +
+

+ +
+ +

- add_query_arg( array( 'edit_group' => '-1' ), admin_url( 'admin.php?page=pmpro-userfields' ) ), + 'name' => __( 'Add New Group', 'paid-memberships-pro' ), + 'icon' => 'plus' + ); + + // Display the links. + foreach ( $pmpro_membershiplevels_page_action_links as $pmpro_membershiplevels_page_action_link ) { + + // If the value is not an array, it is not in the correct format. Continue. + if ( ! is_array( $pmpro_membershiplevels_page_action_link ) ) { + continue; + } + + // Figure out CSS classes for the links. + $classes = array(); + $classes[] = 'page-title-action'; + if ( ! empty( $pmpro_membershiplevels_page_action_link['icon'] ) ) { + $classes[] = 'pmpro-has-icon'; + $classes[] = 'pmpro-has-icon-' . esc_attr( $pmpro_membershiplevels_page_action_link['icon'] ); + } + if ( ! empty( $pmpro_membershiplevels_page_action_link['classes'] ) ) { + $classes[] = $pmpro_membershiplevels_page_action_link['classes']; + } + $class = implode( ' ', array_unique( $classes ) ); ?> + + +

+ +
+

+
+ +
+ name, $pre_checkout_field_locations ) && ! in_array( $group->name, $post_checkout_field_locations ) && in_array( $group->name, $ui_groups ); + ?> +
+
+ +
+ + -

- + +

+ +
+ +
+ + +
+
' ); ?> - -

- -
-
- -
-
- - - - - -
-
- -
-
- + // Check if we have settings for this group. + if ( ! empty( $group->description ) ) { + ?> +

description ); ?>

+ name, $ui_groups ) ) { + ?> +

+ +

+ get_fields(); + $has_sortable_fields = count( array_intersect( $ui_fields, wp_list_pluck( $group_fields, 'name' ) ) ) > 1; + ?> + + + + + + + + + + + + + + + + + + + + + name, $ui_fields ) ) { + ?> + + + + + + + + + + + + +
+ +
name );?> + label ); ?> +
+ label + ) + ); + + $delete_nonce_url = wp_nonce_url( + add_query_arg( + [ + 'page' => 'pmpro-userfields', + 'action' => 'delete_field', + 'delete_name' => $field->name, + ], + admin_url( 'admin.php' ) + ), + 'delete_field', + 'pmpro_userfields_nonce' + ); + + $actions = [ + 'edit' => sprintf( + '%3$s', + esc_attr__( 'Edit', 'paid-memberships-pro' ), + esc_url( + add_query_arg( + [ + 'page' => 'pmpro-userfields', + 'edit' => $field->name, + ], + admin_url( 'admin.php' ) + ) + ), + esc_html__( 'Edit', 'paid-memberships-pro' ) + ), + 'delete' => sprintf( + '%3$s', + esc_attr__( 'Delete', 'paid-memberships-pro' ), + 'javascript:pmpro_askfirst(\'' . esc_js( $delete_text ) . '\', \'' . esc_js( $delete_nonce_url ) . '\'); void(0);', + esc_html__( 'Delete', 'paid-memberships-pro' ) + ), + ]; + + $actions_html = []; + + foreach ( $actions as $action => $link ) { + $actions_html[] = sprintf( + '%2$s', + esc_attr( $action ), + $link + ); + } + + if ( ! empty( $actions_html ) ) { + echo implode( ' | ', $actions_html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + ?> +
+
label ); ?> + profile, array( true, 'only' ), true ) ) { + esc_html_e( 'Yes', 'paid-memberships-pro' ); + } elseif ( in_array( $field->profile, array( 'admin', 'only_admin' ), true ) ) { + esc_html_e( 'Yes (only admins)', 'paid-memberships-pro' ); + } else { + esc_html_e( 'No', 'paid-memberships-pro' ); + } + ?> + + profile, array( 'only', 'only_admin' ), true ) ) { + esc_html_e( 'No', 'paid-memberships-pro' ); + } else { + esc_html_e( 'Yes', 'paid-memberships-pro' ); + } + ?> + required ? 'Yes' : 'No';?> + levels ) ) { + esc_html_e( 'No Level Restrictions', 'paid-memberships-pro' ); + } elseif ( 3 >= count( $field->levels ) ) { + $level_names = array(); + foreach ( $field->levels as $level_id ) { + $level = pmpro_getLevel( $level_id ); + if ( ! empty( $level ) ) { + $level_names[] = $level->name; + } + } + echo esc_html( implode( ', ', $level_names ) ); + } else { + // Show a preview with a button to expand and show all levels. + $level_names = array(); + foreach ( $field->levels as $level_id ) { + $level = pmpro_getLevel( $level_id ); + if ( ! empty( $level ) ) { + $level_names[] = $level->name; + } + } + $preview_levels = array_slice( $level_names, 0, 3 ); + echo esc_html( implode( ', ', $preview_levels ) ) . '...'; + ?> + + + + + + + display( empty( $field->default ) ? null : $field->default ); + if(!empty($field->hint)) { + ?> +

hint );?>

+ +
+

+ + ' ); ?> + +

+ name, $ui_groups ) ) { + ?> +
+ + ' . __( 'Delete fields to enable group deletion.', 'paid-memberships-pro' ) . ''; + $delete_link = '#'; + if ( empty( $group_fields) ) { + $delete_url = empty( $group_fields) ? wp_nonce_url( add_query_arg( array( 'delete_name' => $group->name, 'action' => 'delete_group' ), admin_url( 'admin.php?page=pmpro-userfields' ) ), 'delete_group', 'pmpro_userfields_nonce' ) : '#'; + $delete_text = esc_html( + sprintf( + // translators: %s is the Group Name. + __( "Are you sure you want to delete user field group: %s?", 'paid-memberships-pro' ), + $group->label + ) + ); + $delete_link = 'javascript:pmpro_askfirst(\'' . esc_js( $delete_text ) . '\', \'' . esc_js( esc_url( $delete_url ) ) . '\'); void(0);'; + } + ?> + class="button is-destructive pmpro-has-icon pmpro-has-icon-trash" href="" > + +
+ +
+
+ +
+

+ + ' ); ?> + +

+ name = $name; + $this->label = $label; + $this->description = $description; + } + + /** + * Magic getter for read-only properties. + * + * @param string $name The property name. + * @return mixed The property value. + */ + public function __get( $name ) { + if ( isset( $this->$name ) ) { + return $this->$name; + } + + return null; + } + + /** + * Add a field group. + * + * @since TBD + * + * @param string $name The name of the field group. + * @param string|null $label The label for the field group. If NULL, a cleaned version of the name will be used. + * @param string $description The description for the field group. + * + * @return PMPro_Field_Group The field group object. + */ + public static function add( $name, $label = NULL, $description = '' ) { + global $pmpro_field_groups; + + // If the field group already exists, update the label and description. + if ( ! empty( $pmpro_field_groups[ $name ] ) ) { // Looking at global to avoid infinite loop when a group doesn't exist. + $existing_field_group = self::get( $name ); + $existing_field_group->label = $label; + $existing_field_group->description = $description; + + return $existing_field_group; + } + + // If no label is provided, use the name. + if ( empty( $label ) ) { + if ( $name === 'checkout_boxes' ) { + apply_filters( 'pmpro_default_field_group_label', __( 'More Information','paid-memberships-pro' ) ); + } else { + $label = ucwords( str_replace( '_', ' ', $name ) ); + } + } + + // Create a new field group object. + $field_group = new PMPro_Field_Group( $name, $label, $description ); + + // Add the field group to the global array. + $pmpro_field_groups[ $name ] = $field_group; + + return $field_group; + } + + /** + * Get all added field groups. + * + * @since TBD + * + * @return array An array of PMPro_Field_Group objects. + */ + public static function get_all() { + global $pmpro_field_groups; + + if ( empty( $pmpro_field_groups ) ) { + $pmpro_field_groups = array(); + } + + return $pmpro_field_groups; + } + + /** + * Get an added field group by name. + * + * @since TBD + * + * @param string $name The name of the field group. + * @return PMPro_Field_Group The field group object. + */ + public static function get( $name ) { + // Get all field groups. + $field_groups = self::get_all(); + + // If we don't yet have the field group, create it. + if ( empty( $field_groups[ $name ] ) ) { + return self::add( $name ); + } + + // Return the field group. + return $field_groups[ $name ]; + } + + /** + * Get the field group for a field. + * + * @since TBD + * + * @param PMPro_Field $field The field object. + * @return PMPro_Field_Group|null The field group object, or NULL if the field is not in a group. + */ + public static function get_group_for_field( $field ) { + global $pmpro_field_groups; + + if ( empty( $pmpro_field_groups ) ) { + $pmpro_field_groups = array(); + } + + foreach ( $pmpro_field_groups as $field_group ) { + $group_fields = $field_group->get_fields(); + foreach( $group_fields as $group_field ) { + if ( $group_field->name === $field->name ) { + return $field_group; + } + } + } + + return null; + } + + /** + * Get a field by name. + * + * @since TBD + * + * @param string $name The name of the field. + * @return PMPro_Field|null The field object, or NULL if the field is not in a group. + */ + public static function get_field( $name ) { + global $pmpro_user_fields; + + if ( empty( $pmpro_user_fields ) ) { + $pmpro_user_fields = array(); + } + + foreach ( $pmpro_user_fields as $group_name => $fields ) { + if ( isset( $fields[ $name ] ) ) { + return $fields[ $name ]; + } + } + + return null; + } + + /** + * Add a field to this field group. + * + * @since TBD + * + * @param PMPro_Field $field The field object to add. + * @return bool True if the field was added, otherwise false. + */ + public function add_field( $field ) { + global $pmpro_user_fields; + if ( empty( $pmpro_user_fields ) ) { + $pmpro_user_fields = array(); + } + + /** + * Filter the field to add. + * + * @since 2.9.3 + * + * @param PMProField $field The field being added. + * @param string $group_name The name of the group to add the field to. + */ + $field = apply_filters( 'pmpro_add_user_field', $field, $this->name ); + + // Make sure that we have a valid field. + if ( empty( $field ) || ! pmpro_is_field( $field ) ) { + return false; + } + + // Make sure the group is in the global array of fields. + if ( empty( $pmpro_user_fields[ $this->name ] ) ) { + $pmpro_user_fields[ $this->name ] = array(); + } + + // Add the field to the group. + $pmpro_user_fields[ $this->name ][ $field->name ] = $field; + + return true; + } + + /** + * Get all fields in this field group. + * + * @since TBD + * + * @return array An array of PMPro_Field objects. + */ + public function get_fields() { + global $pmpro_user_fields; + if ( empty( $pmpro_user_fields ) ) { + $pmpro_user_fields = array(); + } + + if ( empty( $pmpro_user_fields[ $this->name ] ) ) { + $pmpro_user_fields[ $this->name ] = array(); + } + + return $pmpro_user_fields[ $this->name ]; + } + + /** + * Get all fields to display in a specific context. + * + * @since TBD + * + * @param array $args The arguments for getting the fields. + */ + public function get_fields_to_display( $args = array() ) { + $default_args = array( + 'scope' => 'profile', // The scope of the fields to show. Can be 'profile' or 'checkout'. + 'user_id' => NULL, // The user ID to show the users for. If null, we are showing fields for the current user. + ); + $args = wp_parse_args( $args, $default_args ); + + // Get all fields in this group. + $fields = $this->get_fields(); + + // Get the user ID. + $user_id = empty( $args['user_id'] ) ? get_current_user_id() : $args['user_id']; + + // Get a list of the fields that should be displayed. + $fields_to_display = array(); + foreach ( $fields as $field ) { + // Validate the field for scope. + if ( 'checkout' === $args['scope'] ) { + // At checkout. + // Check if this field should only be shown in the profile. + if ( in_array( $field->profile, array( 'only', 'only_admin' ), true ) ) { + continue; + } + + // Check if this field is for the level being purchased. + // Get the checkout level. + $checkout_level = pmpro_getLevelAtCheckout(); + $chekcout_level_id = ! empty( $checkout_level->id ) ? (int)$checkout_level->id : NULL; + if ( empty( $chekcout_level_id ) ) { + continue; + } + if ( ! empty( $field->levels ) && ! in_array( (int) $chekcout_level_id, $field->levels, true ) ) { + continue; + } + } else { + // In profile. + // Check if this field should ever be shown in the profile. + if ( empty( $field->profile ) ) { + continue; + } + + // Check if this field should only be shown to admins. + if ( ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'pmpro_membership_manager' ) ) && in_array( $field->profile, array( 'admins', 'admin', 'only_admin' ), true ) ) { + continue; + } + + // Check if the user has a level required for this field. + if ( ! empty( $field->levels ) && ! pmpro_hasMembershipLevel( $field->levels, $user_id ) ) { + continue; + } + } + + // Add the field to the list of fields to display. + $fields_to_display[] = $field; + } + + return $fields_to_display; + } + + /** + * Display the field group. + * + * @since TBD + * + * @param array $args The arguments for displaying the fields. + */ + public function display( $args = array() ) { + $default_args = array( + 'markup' => 'card', // The markup to use for the field group. Can be 'card', 'div' or 'table'. + 'scope' => 'profile', // The scope of the fields to show. Can be 'profile' or 'checkout'. + 'show_group_label' => true, // Whether or not to show the field group. + 'prefill_from_request' => false, // Whether or not to prefill the field values from the $_REQUEST array. + 'show_required' => false, // Whether or not to show required fields. + 'user_id' => NULL, // The user ID to show the users for. If null, we are showing fields for the current user. + ); + $args = wp_parse_args( $args, $default_args ); + + // Get the user ID. + $user_id = empty( $args['user_id'] ) ? get_current_user_id() : $args['user_id']; + + // Get the fields to display. + $fields_to_display = $this->get_fields_to_display( $args ); + + // If we don't have any fields to display, don't display the group. + if ( empty( $fields_to_display ) ) { + return; + } + + // Display the field group. + if ( empty( $args['show_group_label'] ) ) { + $group_header = ''; + $group_footer = ''; + } elseif ( $args['markup'] === 'card' ) { + // Get the "header" for the field group. + ob_start(); + ?> +
+
+
+ label ) ) { ?> + +

label ); ?>

+
+ +
+ description ) ) { ?> +
description ); ?>
+ + +
+
+
+
+ +
+
+ label ) ) { ?> + +

label ); ?>

+
+ +
+ description ) ) { ?> +
description ); ?>
+ + +
+
+ +

label ); ?>

+ description ) ) { + ?> +

description ); ?>

+ + + +
+ get_value_from_request() ) { + $value = $field->get_value_from_request(); + } elseif ( ! empty( $user_id ) && metadata_exists( 'user', $user_id, $field->meta_key ) ) { + $value = get_user_meta( $user_id, $field->meta_key, true ); + } elseif ( ! empty( $user_id ) ) { + $userdata = get_userdata( $user_id ); + if ( ! empty( $userdata->{$field->name} ) ) { + $value = $userdata->{$field->name}; + } elseif(isset($field->value)) { + $value = $field->value; + } + } elseif(isset($field->value)) { + $value = $field->value; + } + + if ( $args['markup'] === 'div' || $args['markup'] === 'card' ) { + // Fix divclass. + if ( ! empty( $field->divclass ) ) { + $field->divclass .= " "; + } + + // Add a class to the field based on the type. + $field->divclass .= "pmpro_form_field pmpro_form_field-" . $field->type; + $field->class .= " pmpro_form_input-" . $field->type; + + // Add the required class to field. + if ( ! empty( $args['show_required'] ) && ! empty( $field->required ) ) { + $field->divclass .= " pmpro_form_field-required"; + $field->class .= " pmpro_form_input-required"; + } + + // Add the class to not show a field is required if set. + if ( ! empty( $args['show_required'] ) && ( empty( $field->showrequired ) || is_string( $field->showrequired ) ) ) { + $field->divclass .= " pmpro_form_field-hide-required"; + } + + // Run the class through the filter. + $field->divclass = pmpro_get_element_class( $field->divclass ); + $field->class = pmpro_get_element_class( $field->class ); + + ?> +
divclass ) ) { echo 'class="' . esc_attr( $field->divclass ) . '"'; } ?>> + showmainlabel)) { ?> + + display( $value ); ?> + + display( $value ); ?> + + + hint)) { ?> +

hint );?>

+ +
+ + + + showmainlabel ) ) { ?> + + + + + display($value); + else + echo "
" . wp_kses_post( $field->displayValue($value) ) . "
"; + ?> + hint)) { ?> +

hint );?>

+ + + + 'profile', // The scope of the fields to save. Can be 'profile' or 'checkout'. + 'user_id' => NULL, // The user ID to save the users for. If null, we are saving fields for the current user. + ); + $args = wp_parse_args( $args, $default_args ); + + // Get the user ID if needed. + $user_id = empty( $args['user_id'] ) ? get_current_user_id() : $args['user_id']; + + // Make sure the current user can edit this user. + if ( 'scope' == 'profile' && ! current_user_can( 'edit_user', $user_id ) ) { + return false; + } + + // Get the fields to display. + $fields_to_display = $this->get_fields_to_display( $args ); + + // Save the fields. + foreach ( $fields_to_display as $field ) { + $field->save_field_for_user( $user_id ); + } + + return true; + } +} \ No newline at end of file diff --git a/classes/class-pmpro-field.php b/classes/class-pmpro-field.php index b23fa1009..50b0984e7 100755 --- a/classes/class-pmpro-field.php +++ b/classes/class-pmpro-field.php @@ -10,7 +10,7 @@ class PMPro_Field { * * @var string */ - public $name = ''; + private $name = ''; /** * The type of field that this is. @@ -19,7 +19,7 @@ class PMPro_Field { * * @var string */ - public $type = ''; + private $type = ''; /** * The meta key for this field. @@ -30,7 +30,7 @@ class PMPro_Field { * * @var string */ - public $meta_key = ''; + private $meta_key = ''; /** * The label of the field. @@ -41,7 +41,7 @@ class PMPro_Field { * * @var string */ - public $label = ''; + private $label = ''; /** * Whether the label should be shown. @@ -50,7 +50,7 @@ class PMPro_Field { * * @var bool */ - public $showmainlabel = true; + private $showmainlabel = true; /** * A hint to be displayed with the field. @@ -59,7 +59,7 @@ class PMPro_Field { * * @var string */ - public $hint = ''; + private $hint = ''; /** * The membership levels that this field should be displayed for. @@ -68,7 +68,7 @@ class PMPro_Field { * * @var array */ - public $levels = array(); + private $levels = array(); /** * Whether the field is required. @@ -77,7 +77,7 @@ class PMPro_Field { * * @var bool */ - public $required = false; + private $required = false; /** * Whether the field should be shown as required if $required is set to true. @@ -86,7 +86,7 @@ class PMPro_Field { * * @var bool */ - public $showrequired = true; + private $showrequired = true; /** * Where this field should be shown. @@ -97,7 +97,7 @@ class PMPro_Field { * * @var mixed */ - public $profile = true; + private $profile = true; /** * Whether the field is readonly. @@ -106,7 +106,7 @@ class PMPro_Field { * * @var bool */ - public $readonly = false; + private $readonly = false; /** * Array to define conditions when a field should be shown or hidden. @@ -115,7 +115,7 @@ class PMPro_Field { * * @var array */ - public $depends = array(); + private $depends = array(); /** * Flag to determine if depends conditions should be ANDed or ORed together. @@ -124,7 +124,7 @@ class PMPro_Field { * * @var bool */ - public $depends_or = false; + private $depends_or = false; /** * Whether the field value should be sanitized before saving. @@ -133,7 +133,7 @@ class PMPro_Field { * * @var bool */ - public $sanitize = true; + private $sanitize = true; /** * The ID to show for the field. @@ -142,7 +142,7 @@ class PMPro_Field { * * @var string */ - public $id = ''; + private $id = ''; /** * Class for the input field. @@ -151,7 +151,7 @@ class PMPro_Field { * * @var string */ - public $class = ''; + private $class = ''; /** * Class for the div wrapper for the input field. @@ -160,7 +160,7 @@ class PMPro_Field { * * @var string */ - public $divclass = ''; + private $divclass = ''; /** * Whether this field should be included in a members list CSV export. @@ -169,7 +169,7 @@ class PMPro_Field { * * @var bool */ - public $memberslistcsv = false; + private $memberslistcsv = false; /** * The save function that should be used for this field. @@ -180,7 +180,7 @@ class PMPro_Field { * * @var callable */ - public $save_function = null; + private $save_function = null; /** * Whether this field should be shown when adding a member using @@ -190,7 +190,7 @@ class PMPro_Field { * * @var bool */ - public $addmember = false; + private $addmember = false; /** * The size attribute when using a text input field type. @@ -199,7 +199,7 @@ class PMPro_Field { * * @var int */ - public $size = 30; + private $size = 30; /** * The number of rows to show when using a textarea field type. @@ -208,7 +208,7 @@ class PMPro_Field { * * @var int */ - public $rows = 5; + private $rows = 5; /** * The number of columns to show when using a textarea field type. @@ -217,7 +217,7 @@ class PMPro_Field { * * @var int */ - public $cols = 80; + private $cols = 80; /** * The options for a select, select2, multiselect, checkbox_grouped, or radio field type. @@ -226,7 +226,7 @@ class PMPro_Field { * * @var array */ - public $options = array(); + private $options = array(); /** * Whether multiple options should be selectable when using a select, select2, or multiselect field type. @@ -235,7 +235,7 @@ class PMPro_Field { * * @var bool */ - public $multiple = false; + private $multiple = false; /** * The text to show next to a checkbox when using a checkbox field type. @@ -244,7 +244,7 @@ class PMPro_Field { * * @var string */ - public $text = ''; + private $text = ''; /** * The HTML to show for an HTML field type. @@ -253,7 +253,7 @@ class PMPro_Field { * * @var string */ - public $html = ''; + private $html = ''; /** * The default value for a field. @@ -262,7 +262,7 @@ class PMPro_Field { * * @var string */ - public $default = ''; + private $default = ''; /** * File upload types. @@ -272,7 +272,7 @@ class PMPro_Field { * @var string * */ - public $allowed_file_types = ''; + private $allowed_file_types = ''; /** * File upload limit @@ -281,7 +281,7 @@ class PMPro_Field { * * @var int */ - public $max_file_size = ''; + private $max_file_size = ''; function __construct($name = NULL, $type = NULL, $attr = NULL) { if ( ! empty( $name ) ) @@ -290,6 +290,137 @@ function __construct($name = NULL, $type = NULL, $attr = NULL) { return true; } + /** + * Magic getter to allow reading private class properties. + * + * @param string $name The property name. + * @return mixed The property value. + */ + function __get( $name ) { + if ( isset( $this->$name ) ) { + if ( ! $this->is_valid_property( $name ) ) { + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The property %s is not valid for the field type %s.', 'paid-memberships-pro' ), $name, $this->type ), 'TBD' ); + } + return $this->$name; + } else { + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The property %s does not exist.', 'paid-memberships-pro' ), $name ), 'TBD' ); + } + + return null; + } + + /** + * Magic setter to allow setting private class properties and + * throwing warnings when we want to phase out a property. + * + * @param string $name The property name. + * @param mixed $value The property value. + */ + function __set( $name, $value ) { + if ( 'type' === $name ) { + _doing_it_wrong( __FUNCTION__, esc_html__( 'PMPro_Field properties should not be modified directly and may break in a future version. Instead, create a new PMPro_Field object.', 'paid-memberships-pro' ), 'TBD' ); + } + + $this->$name = $value; + } + + /** + * Magic isset to check if a private class property is set. + * + * @param string $name The property name. + * @return bool Whether the property is set. + */ + function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Magic __call to allow calling private class methods and throwing warnings + * when we want to phase out a method. + */ + function __call( $name, $arguments ) { + switch( $name ) { + case 'set': + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The method %s of PMPro_Field has become private and will not be available in a future version. Instead, use the $args property of the constructor when creating a new PMPro_Field object.', 'paid-memberships-pro' ), $name ), 'TBD' ); + break; + case 'saveUsersTable': + case 'saveTermRelationshipsTable': + case 'saveFile': + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The method %s of PMPro_Field has become private and will not be available in a future version. Instead, use the save_field_for_user method of the PMPro_Field object.', 'paid-memberships-pro' ), $name ), 'TBD' ); + break; + case 'getHTML': + case 'getDependenciesJS': + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The method %s of PMPro_Field has become private and will not be available in a future version. Instead, use the display() method of the PMPro_Field object.', 'paid-memberships-pro' ), $name ), 'TBD' ); + break; + default: + _doing_it_wrong( __FUNCTION__, sprintf( esc_html__( 'The method %s of PMPro_Field has become private and will not be available in a future version.', 'paid-memberships-pro' ), $name ), 'TBD' ); + break; + } + return call_user_func( array( $this, $name ), $arguments ); + } + + /** + * Check if a property should be present for the current field type. + * + * @since TBD + * + * @param string $property The property to check. + * return bool Whether the property is valid for the field type. + */ + private function is_valid_property( $property ) { + switch ( $property ) { + case 'name': + case 'type': + case 'meta_key': + case 'label': + case 'showmainlabel': + case 'hint': + case 'levels': + case 'required': + case 'showrequired': + case 'profile': + case 'readonly': + case 'depends': + case 'depends_or': + case 'sanitize': + case 'id': + case 'class': + case 'divclass': + case 'memberslistcsv': + case 'save_function': + case 'addmember': + case 'default': + return true; + break; + case 'size': + return in_array( $this->type, array( 'text', 'number' ) ); + break; + case 'rows': + case 'cols': + return 'textarea' === $this->type; + break; + case 'options': + return in_array( $this->type, array( 'select', 'multiselect', 'select2', 'radio', 'checkbox_grouped' ) ); + break; + case 'multiple': + return in_array( $this->type, array( 'select', 'select2', 'multiselect' ) ); + break; + case 'text': + return 'checkbox' === $this->type; + break; + case 'html': + return 'html' === $this->type; + break; + case 'allowed_file_types': + case 'max_file_size': + return 'file' === $this->type; + break; + default: + return false; + break; + } + } + /* setup field based on passed values attr is array of one or more of the following: @@ -300,7 +431,7 @@ function __construct($name = NULL, $type = NULL, $attr = NULL) { - just_profile = bool (not required. true means only show field in profile) - class = string (class to add to html element) */ - function set($name, $type, $attr = array()) + private function set($name, $type, $attr = array()) { $this->name = $name; $this->type = $type; @@ -468,8 +599,86 @@ function set($name, $type, $attr = array()) return true; } + /** + * Get the field value from $_REQEUST or $_SESSION. + * The value will be sanitized if the field has the sanitize property set to true. + * + * @since TBD + * + * @return mixed The value of the field or null if not found. + */ + function get_value_from_request() { + if ( isset( $_REQUEST[ $this->name ] ) ) { + $value = $_REQUEST[$this->name]; + } elseif ( isset( $_REQUEST[ $this->name . '_checkbox' ] ) && $this->type == 'checkbox' ) { + // Empty checkbox. + $value = 0; + } elseif ( ! empty( $_REQUEST[ $this->name . '_checkbox' ] ) && in_array( $this->type, array( 'checkbox_grouped', 'select2' ) ) ) { + // Empty group checkboxes or select2. + $value = array(); + } elseif ( isset( $_FILES[$this->name] ) && $this->type == 'file' ) { + // File field. + $value = $_FILES[$this->name]['name']; + } elseif ( isset( $_SESSION[$this->name] ) ) { + // Value stored in session. + if ( is_array( $_SESSION[$this->name] ) && isset( $_SESSION[$this->name]['name'] ) ) { + // File field in session. + $_FILES[$this->name] = $_SESSION[$this->name]; + $value = $_SESSION[$this->name]['name']; + } else { + // Other field in session. + $value = $_SESSION[$this->name]; + } + + // Clean up session. + unset($_SESSION[$this->name]); + } else { + // No value found. + return null; + } + + // Sanitize the value if needed. + if ( ! empty( $field->sanitize ) ) { + if ( $this->type == 'textarea' ) { + $value = sanitize_textarea_field( $value ); + } elseif ( is_array( $value ) ) { + $value = array_map( 'sanitize_text_field', $value ); + } else { + $value = sanitize_text_field( $value ); + } + } + + return $value; + } + + /** + * Save the field for a user. + * + * @since TBD + * + * @param int $user_id The user ID to save the field for. + */ + function save_field_for_user( int $user_id ) { + // Get the value of the field. + $value = $this->get_value_from_request(); + + // If field was not submitted, bail. + if ( null === $value ) { + return; + } + + // Check if we have a save function. + if ( ! empty( $this->save_function ) ) { + // Call the save function. + call_user_func( $this->save_function, $user_id, $this->name, $value, $this ); + } else { + // Save the value to usermeta. + update_user_meta($user_id, $this->meta_key, $value); + } + } + // Save function for users table field. - function saveUsersTable( $user_id, $name, $value ) { + private function saveUsersTable( $user_id, $name, $value ) { // Special sanitization needed for certain user fields. if ( $name === 'user_url' ) { $value = esc_url_raw( $value ); @@ -483,7 +692,7 @@ function saveUsersTable( $user_id, $name, $value ) { } // Save function for user taxonomy field. - function saveTermRelationshipsTable( $user_id, $name, $value ) { + private function saveTermRelationshipsTable( $user_id, $name, $value ) { // Get the taxonomy to save for. if ( isset( $this->taxonomy ) ) { $taxonomy = $this->taxonomy; @@ -512,7 +721,7 @@ function saveTermRelationshipsTable( $user_id, $name, $value ) { } //save function for files - function saveFile($user_id, $name, $value) + private function saveFile($user_id, $name, $value) { //setup some vars $user = get_userdata($user_id); @@ -649,15 +858,17 @@ function saveFile($user_id, $name, $value) update_user_meta($user_id, $meta_key, $file_meta_value_array ); } - //echo the HTML for the field - function display($value = NULL) - { - echo $this->getHTML($value); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + /** + * Display the field. + */ + function display( $value = NULL ) { + echo $this->getHTML( $value ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + $this->getDependenciesJS(); return; } //get HTML for the field - function getHTML($value = "") + private function getHTML($value = "") { // Vars to store HTML to be added to the beginning or end. $r_beginning = ''; @@ -683,7 +894,7 @@ function getHTML($value = "") if($this->type == "text") { - $r = 'id ) . '" name="' . esc_attr( $this->name ) . '" value="' . ( is_string( $value ) ? esc_attr(wp_unslash($value) ) : '' ) . '" '; if(!empty($this->size)) $r .= 'size="' . esc_attr( $this->size ) . '" '; if(!empty($this->class)) @@ -909,7 +1120,7 @@ function getHTML($value = "") $r .= 'readonly="readonly" '; if(!empty($this->html_attributes)) $r .= $this->getHTMLAttributes(); - $r .= '>' . esc_textarea(wp_unslash($value)) . ''; + $r .= '>' . ( ( is_string( $value ) ) ? esc_textarea(wp_unslash($value) ) : '' ) . ''; } elseif($this->type == "hidden") { @@ -934,35 +1145,32 @@ function getHTML($value = "") { $r = ''; - //old value - if(is_user_logged_in()) - { - global $current_user; - $old_value = get_user_meta($current_user->ID, $this->meta_key, true); - if(!empty($old_value)) - $r .= ''; - } - // Show the existing file with a preview and allow user to delete or replace. - if ( ! empty( $value ) ) { + if ( ! empty( $value ) && ( is_array( $value ) || ! empty( $this->file ) ) ) { + if ( is_array( $value ) ) { + $file = $value; + } elseif ( ! empty( $this->file ) ) { + // Legacy support for $this->file. + $file = $this->file; + } // Show a preview of existing file if image type. - if ( ( ! empty( $this->preview ) ) && ! empty( $this->file['previewurl'] ) ) { - $filetype = wp_check_filetype( basename( $this->file['previewurl'] ), null ); + if ( ( ! empty( $this->preview ) ) && ! empty( $file['previewurl'] ) ) { + $filetype = wp_check_filetype( basename( $file['previewurl'] ), null ); if ( $filetype && 0 === strpos( $filetype['type'], 'image/' ) ) { - $r_beginning .= '
' . esc_attr( basename($value) ) . '
'; + $r_beginning .= '
' . esc_attr( basename($file['filename']) ) . '
'; } } - if( ! empty( $this->file['fullurl'] ) ) { - $r_beginning .= '
' . sprintf(__('Current File: %s', 'paid-memberships-pro' ), '' . esc_html( basename($value) ) . '' ) . '
'; - } else { + if( ! empty( $file['fullurl'] ) ) { + $r_beginning .= '
' . sprintf(__('Current File: %s', 'paid-memberships-pro' ), '' . esc_html( basename($file['filename']) ) . '' ) . '
'; + } elseif( is_string( $value ) ) { $r_beginning .= sprintf(__('Current File: %s', 'paid-memberships-pro' ), basename($value) ); } $r_beginning .= '
'; // Allow user to delete the uploaded file if we know the full location. - if ( ( ! empty( $this->allow_delete ) ) && ! empty( $this->file['fullurl'] ) ) { + if ( ( ! empty( $this->allow_delete ) ) && ! empty( $file['fullurl'] ) ) { // Check whether the current user can delete the uploaded file based on the field attribute 'allow_delete'. if ( $this->allow_delete === true || ( $this->allow_delete === 'admins' || $this->allow_delete === 'only_admin' && current_user_can( 'manage_options' ) ) @@ -977,48 +1185,52 @@ function getHTML($value = "") $r_beginning .= ''; } $r_beginning .= '
'; - } + //include script to change enctype of the form and allow deletion + $r .= ' + + '; + } - }); - - '; - - $r .= '
'; + $r .= ' + + '; + $r .= '
'; $r .= 'allowed_file_types ) ) { @@ -1164,7 +1376,7 @@ function getHTMLAttributes() { return $html; } - function getDependenciesJS() + private function getDependenciesJS() { global $pmpro_user_fields; //dependencies @@ -1172,27 +1384,44 @@ function getDependenciesJS() { //build the checks $checks_escaped = array(); + $binds = array(); foreach($this->depends as $check) { if(!empty($check['id'])) { // If checking checkbox_grouped, need to update the $check['id'] with index of option. $field_id = $check['id']; - $depends_checkout_box = PMPro_Field::get_checkout_box_name_for_field( $field_id ); - if ( empty( $depends_checkout_box ) ) { + $depends_field = PMPro_Field_Group::get_field( $field_id ); + if ( empty( $depends_field ) ) { continue; } - foreach ( $pmpro_user_fields[ $depends_checkout_box ] as $field ) { - if ( $field->type === 'checkbox_grouped' && $field->name === $field_id && ! empty( $field->options ) ) { - $field_id = $field_id . '_' . intval( array_search( $check['value'], array_keys( $field->options ) )+1 ); - } + + $depends_field_group = PMPro_Field_Group::get_group_for_field( $depends_field ); + if ( empty( $depends_field_group ) ) { + continue; } - $checks_escaped[] = "((jQuery('#" . esc_html( $field_id ) ."')".".is(':checkbox')) " - ."? jQuery('#" . esc_html( $field_id ) . ":checked').length > 0" - .":(jQuery('#" . esc_html( $field_id ) . "').val() == " . json_encode($check['value']) . " || jQuery.inArray( jQuery('#" . esc_html( $field_id ) . "').val(), " . json_encode($check['value']) . ") > -1)) ||"."(jQuery(\"input:radio[name='". esc_html( $check['id'] ) ."']:checked\").val() == ".json_encode($check['value'])." || jQuery.inArray(".json_encode($check['value']).", jQuery(\"input:radio[name='". esc_html( $field_id ) ."']:checked\").val()) > -1)"; - - $binds[] = "#" . esc_html( $field_id ) .",input:radio[name=". esc_html( $field_id ) ."]"; + // Let's simplify. + switch ( $depends_field->type ) { + case 'checkbox_grouped': + // Find an input with the name of the field and the value of the option, then check if it is selected. + // Don't use the ID. + $checks_escaped[] = "jQuery('input[name=\"" . esc_js( $field_id ) . "[]\"][value=" . esc_js( $check['value'] ) . "]:checked').length > 0"; + $binds[] = "input[name=\"" . esc_js( $field_id ) . "[]\"]"; + break; + case 'checkbox': + $checks_escaped[] = "jQuery('#" . esc_html( $field_id ) . "').is(':checked') == " . ( empty( $check['value'] ) ? 'false' : 'true' ); + $binds[] = "#" . esc_html( $field_id ); + break; + case 'radio': + $checks_escaped[] = "jQuery('#" . esc_html( $field_id ) . " input[type=radio][value=" . esc_js( $check['value'] ) . "]:checked').length > 0"; + $binds[] = "#" . esc_html( $field_id ) . " input[type=radio][name=" . esc_js( $field_id ) . "]"; + break; + default: + $checks_escaped[] = "jQuery('#" . esc_html( $field_id ) . "').val() == " . json_encode( $check['value'] ) . " || jQuery.inArray( jQuery('#" . esc_html( $field_id ) . "').val(), " . json_encode( $check['value'] ) . ") > -1"; + $binds[] = "#" . esc_html( $field_id ); + break; + } } } @@ -1245,33 +1474,20 @@ function pmpro_id );?>_hideshow() { } } + /** + * Display the field at checkout. + * + * @deprecated TBD Use PMPro_Field_Group::display() instead. + */ function displayAtCheckout() { + _deprecated_function( __METHOD__, 'TBD', 'PMPro_Field_Group::display()' ); global $current_user; - if(isset($_REQUEST[$this->name])) { - $value = pmpro_sanitize( $_REQUEST[$this->name], $this ); - } elseif(isset($_SESSION[$this->name])) { - //file or value? - if(is_array($_SESSION[$this->name]) && !empty($_SESSION[$this->name]['name'])) - { - $_FILES[$this->name] = $_SESSION[$this->name]; - $this->file = pmpro_sanitize( $_SESSION[$this->name]['name'], $this ); - $value = pmpro_sanitize( $_SESSION[$this->name]['name'], $this ); - } else { - $value = pmpro_sanitize( $_SESSION[$this->name], $this ); - } - } - elseif(!empty($current_user->ID) && metadata_exists("user", $current_user->ID, $this->meta_key)) - { - $meta = get_user_meta($current_user->ID, $this->meta_key, true); - if(is_array($meta) && !empty($meta['filename'])) - { - $this->file = get_user_meta($current_user->ID, $this->meta_key, true); - $value = $this->file['filename']; - } else { - $value = $meta; - } + if( null !== $this->get_value_from_request() ) { + $value = $this->get_value_from_request(); + } elseif(!empty($current_user->ID) && metadata_exists("user", $current_user->ID, $this->meta_key)) { + $value = get_user_meta($current_user->ID, $this->meta_key, true); } elseif ( ! empty( $current_user->ID ) ) { $userdata = get_userdata( $current_user->ID ); if ( ! empty( $userdata->{$this->name} ) ) { @@ -1333,23 +1549,18 @@ function displayAtCheckout()
getDependenciesJS(); } + /** + * @deprecated TBD Use PMPro_Field_Group::display() instead. + */ function displayInProfile($user_id, $edit = NULL) { + _deprecated_function( __METHOD__, 'TBD', 'PMPro_Field_Group::display()' ); global $current_user; if(metadata_exists("user", $user_id, $this->meta_key)) { - $meta = get_user_meta($user_id, $this->meta_key, true); - if(is_array($meta) && !empty($meta['filename'])) - { - $this->file = get_user_meta($user_id, $this->meta_key, true); - $value = $this->file['filename']; - } - else - $value = $meta; + $value = get_user_meta($user_id, $this->meta_key, true); } elseif(!empty($this->value)) $value = $this->value; @@ -1375,8 +1586,6 @@ function displayInProfile($user_id, $edit = NULL) getDependenciesJS(); } /** @@ -1456,28 +1665,34 @@ function displayValue( $value, $echo = true ) { * @param array $array The array to check if it is associative. * @return bool True if the array is associative, false otherwise. */ - function is_assoc( $array ) { + private function is_assoc( $array ) { if ( empty( $array ) ) { return false; } return array_keys( $array ) !== range( 0, count( $array ) - 1) ; } + /** + * @deprecated TBD Use PMPro_Field_Group::get_group_for_field() instead. + */ static function get_checkout_box_name_for_field( $field_name ) { - global $pmpro_user_fields; - foreach( $pmpro_user_fields as $checkout_box_name => $fields ) { - foreach($fields as $field) { - if( $field->name == $field_name ) { - return $checkout_box_name; - } - } + _deprecated_function( __METHOD__, 'TBD', 'PMPro_Field_Group::get_group_for_field()' ); + $field = PMPro_Field_Group::get_field( $field_name ); + if ( empty( $field ) ) { + return ''; } - return ''; + + $field_group = PMPro_Field_Group::get_group_for_field( $field ); + return $field_group ? $field_group->name : ''; } + /** + * @deprecated TBD + */ function was_present_on_checkout_page() { + _deprecated_function( __METHOD__, 'TBD' ); // Check if checkout box that field is in is on page. - $checkout_box = PMPro_Field::get_checkout_box_name_for_field( $this->name ); + $checkout_box = PMPro_Field_Group::get_group_for_field( $this ); if ( empty( $checkout_box ) ) { // Checkout box does not exist. return false; @@ -1489,7 +1704,7 @@ function was_present_on_checkout_page() { 'after_email', 'after_captcha', ); - if ( is_user_logged_in() && in_array( $checkout_box, $user_fields_locations ) ) { + if ( is_user_logged_in() && in_array( $checkout_box->name, $user_fields_locations ) ) { // User is logged in and field is only for new users. return false; } @@ -1532,23 +1747,70 @@ function was_present_on_checkout_page() { return true; } + /** + * Check if the field was filled if needed. + */ function was_filled_if_needed() { - // If field is never required or is not present on checkout page, return true. - if ( ! $this->required || ! $this->was_present_on_checkout_page() ) { + // If the field is not required, skip it. + if ( empty( $field->required ) ) { return true; } - // Return whether the field is filled. + // If this field has a 'depends` attribute, check if the field was actually shown. + if ( ! empty( $this->depends ) ) { + foreach ( $this->depends as $check ) { + // If the $check object isn't valid, skip it. + if ( ! isset( $check->id ) || ! isset( $check->value ) ) { + return true; + } + + // Get the field to check. + $check_field = PMPro_Field_Group::get_field( $check->id ); + if ( empty( $check_field ) ) { + // The check field doesn't exist, so this field wasn't shown. + return true; + } + + // Get the value of the field. + $check_field_value = $check_field->get_value_from_request(); + if ( null === $check_field_value ) { + // The check field wasn't submitted, so this field wasn't shown. + return true; + } + + // If $check['value'] doesn't match the value of the field, skip this field. + if ( is_array( $check_field_value ) ) { + if ( ! in_array( $check->value, $check_field_value ) ) { + return true; + } + } else { + if ( $check->value !== $check_field_value ) { + return true; + } + } + } + } + + // At this point, we know that the field needs to be filled. + $value = $this->get_value_from_request(); switch ( $this->type ) { case 'text': case 'textarea': case 'number': - $filled = ( isset( $_REQUEST[$this->name] ) && '' !== trim( $_REQUEST[$this->name] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $filled = ( null !== $value && '' !== trim( $value ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + break; + case 'file': + if ( ! empty( $_FILES[ $this->name ]['name'] ) ) { + $filled = true; + } elseif ( ! empty( get_user_meta( get_current_user_id(), $this->name, true ) ) && empty( $_REQUEST['pmpro_delete_file_' . $this->name . '_field'] ) ) { + $filled = true; + } else { + $filled = false; + } break; default: - $filled = ! ( empty( $_REQUEST[$this->name] ) && empty( $_FILES[$this->name]['name'] ) && empty( $_REQUEST[$this->name.'_old'] ) ); + $filled = ! empty( $value ); } - return $filled; } } \ No newline at end of file diff --git a/includes/fields.php b/includes/fields.php index b176e077f..51f8fe7e4 100644 --- a/includes/fields.php +++ b/includes/fields.php @@ -1,15 +1,4 @@ name = 'checkout_boxes'; -$cb->label = apply_filters( 'pmpro_default_field_group_label', 'More Information' ); -$cb->order = 0; -$pmpro_field_groups = array( 'checkout_boxes' => $cb ); - /** * Check if a variable is a PMPro_Field. * Also checks for PMProRH_Field. @@ -36,37 +25,22 @@ function pmpro_is_field( $var ) { * - just_profile (make sure you set the profile attr of the field to true or admins) */ function pmpro_add_user_field( $where, $field ) { - global $pmpro_user_fields; - /** * Filter the group to add the field to. * * @since 2.9.3 + * @deprecated TBD * * @param string $where The name of the group to add the field to. * @param PMPro_Field $field The field being added. */ - $where = apply_filters( 'pmpro_add_user_field_where', $where, $field ); - - /** - * Filter the field to add. - * - * @since 2.9.3 - * - * @param PMPro_Field $field The field being added. - * @param string $where The name of the group to add the field to. - */ - $field = apply_filters( 'pmpro_add_user_field', $field, $where ); - - if(empty($pmpro_user_fields[$where])) { - $pmpro_user_fields[$where] = array(); - } - if ( ! empty( $field ) && pmpro_is_field( $field ) ) { - $pmpro_user_fields[$where][] = $field; - return true; - } + $where = apply_filters_deprecated( 'pmpro_add_user_field_where', array( $where, $field ), 'TBD', 'pmpro_add_user_field' ); + + // Get the field group. + $field_group = PMPro_Field_Group::get( $where ); - return false; + // Add the field to the group. + $field_group->add_field( $field ); } /** @@ -77,34 +51,7 @@ function pmpro_add_user_field( $where, $field ) { * Name must contain no spaces or special characters. */ function pmpro_add_field_group( $name, $label = NULL, $description = '', $order = NULL ) { - global $pmpro_field_groups; - // Bail if the group already exists. - foreach ( $pmpro_field_groups as $group ) { - if ( $group->name === $name ) { - // Group already exists. - return false; - } - } - - $temp = new stdClass(); - $temp->name = $name; - $temp->label = $label; - $temp->description = $description; - $temp->order = $order; - - //defaults - if( empty( $temp->label ) ) { - $temp->label = ucwords($temp->name); - } - if( ! isset( $order ) ) { - $lastbox = pmpro_array_end( $pmpro_field_groups ); - $temp->order = $lastbox->order + 1; - } - - $pmpro_field_groups[$name] = $temp; - usort( $pmpro_field_groups, 'pmpro_sort_by_order' ); - - return true; + return PMPro_Field_Group::add( $name, $label, $description ); } /** @@ -208,17 +155,12 @@ function pmpro_add_user_taxonomy( $name, $name_plural ) { /** * Get a field group by name. + * + * @deprecated TBD Use PMPro_Field_Group::get instead. */ function pmpro_get_field_group_by_name( $name ) { - global $pmpro_field_groups; - if( ! empty( $pmpro_field_groups ) ) { - foreach( $pmpro_field_groups as $group ) { - if( $group->name == $name ) { - return $group; - } - } - } - return false; + _deprecated_function( __FUNCTION__, 'TBD', 'PMPro_Field_Group::get' ); + return PMPro_Field_Group::get( $name ); } /** @@ -254,29 +196,58 @@ function pmpro_check_field_for_level( $field, $scope = 'default', $args = NULL ) return true; } +/** + * Get a list of all fields that are only shown when creating a user at checkout. + */ +function pmpro_get_user_creation_field_groups() { + return array( + 'after_username', + 'after_password', + 'after_email', + ); +} + /** * Find fields in a group and display them at checkout. + * This function is only used for the following fields at checkout: + * - after_username + * - after_password + * - after_email + * - after_captcha + * - checkout_boxes + * - after_billing_fields + * - before_submit_button + * - after_tos_fields */ function pmpro_display_fields_in_group( $group, $scope = 'checkout' ) { - global $pmpro_user_fields; - - if( ! empty( $pmpro_user_fields[$group] ) ) { - foreach( $pmpro_user_fields[$group] as $field ) { - if ( ! pmpro_is_field( $field ) ) { - continue; - } - - if ( ! pmpro_check_field_for_level( $field ) ) { - continue; - } - - if ( $scope == 'checkout' ) { - if( ! isset( $field->profile ) || $field->profile !== 'only' && $field->profile !== 'only_admin' ) { - $field->displayAtCheckout(); - } - } - } + $valid_groups = array( + 'after_username', + 'after_password', + 'after_pricing_fields', + 'after_email', + 'after_captcha', + 'after_billing_fields', + 'before_submit_button', + 'after_tos_fields', + ); + if ( ! in_array( $group, $valid_groups ) ) { + _doing_it_wrong( __FUNCTION__, sprintf( __( 'The group %s should not be passed into %s. Use PMPro_Field_Group::display() instead.', 'paid-memberships-pro' ), $group, __FUNCTION__ ), '2.9.3' ); } + if ( $scope !== 'checkout' ) { + _doing_it_wrong( __FUNCTION__, sprintf( __( 'The scope %s should not be passed into %s. Use PMPro_Field_Group::display() instead.', 'paid-memberships-pro' ), $scope, __FUNCTION__ ), '2.9.3' ); + } + + // Get the field group. + $field_group = PMPro_Field_Group::get( $group ); + $field_group->display( + array( + 'markup' => 'div', + 'scope' => 'checkout', + 'show_group_label' => false, + 'prefill_from_request' => true, + 'show_required' => true, + ) + ); } /** @@ -308,44 +279,30 @@ function pmpro_checkout_after_captcha_fields() { //checkout boxes function pmpro_checkout_boxes_fields() { - global $pmpro_user_fields, $pmpro_field_groups; + // Get all field groups. + $field_groups = PMPro_Field_Group::get_all(); - foreach($pmpro_field_groups as $cb) - { - //how many fields to show at checkout? - $n = 0; - if(!empty($pmpro_user_fields[$cb->name])) - foreach($pmpro_user_fields[$cb->name] as $field) - if(pmpro_is_field($field) && pmpro_check_field_for_level($field) && (!isset($field->profile) || (isset($field->profile) && $field->profile !== "only" && $field->profile !== "only_admin"))) $n++; + $checkout_level = pmpro_getLevelAtCheckout(); + $chekcout_level_id = ! empty( $checkout_level->id ) ? (int)$checkout_level->id : NULL; + if ( empty( $chekcout_level_id ) ) { + return; + } - if($n > 0) { - ?> -
-
-
- label ) ) { ?> - -

label ); ?>

-
- -
- description ) ) { ?> -
description ); ?>
- - - name] as $field) { - if( pmpro_is_field($field) && pmpro_check_field_for_level($field) && (!isset($field->profile) || (isset($field->profile) && $field->profile !== "only" && $field->profile !== "only_admin"))) { - $field->displayAtCheckout(); - } - } - ?> -
-
-
-
- $field_group ) { + // If this is not a checkout box, skip it. + if ( in_array( $field_group_name, array( 'after_username', 'after_password', 'after_email', 'after_captcha', 'after_pricing_fields', 'after_billing_fields', 'before_submit_button', 'after_tos_fields' ) ) ) { + continue; } + + $field_group->display( + array( + 'markup' => 'card', + 'scope' => 'checkout', + 'prefill_from_request' => true, + 'show_required' => true, + ) + ); } } add_action( 'pmpro_checkout_boxes', 'pmpro_checkout_boxes_fields' ); @@ -375,94 +332,124 @@ function pmpro_checkout_after_tos_fields() { add_action( 'pmpro_checkout_before_submit_button', 'pmpro_checkout_after_tos_fields', 6 ); /** - * Update the fields at checkout. + * Update user creation fields at checkout after a user is created. + * + * @since TBD + * + * @param int $user_id The ID of the user that was created. */ -function pmpro_after_checkout_save_fields( $user_id, $order ) { - global $pmpro_user_fields; +function pmpro_checkout_before_user_auth_save_fields( $user_id ) { + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + $user_creation_field_groups = pmpro_get_user_creation_field_groups(); + foreach($field_groups as $group_name => $group) { + if ( ! in_array( $group_name, $user_creation_field_groups ) ) { + continue; + } - //any fields? - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) - { - //cycle through fields - foreach($fields as $field) - { - if( ! pmpro_is_field( $field ) ) { - continue; - } - - if ( ! pmpro_check_field_for_level( $field, "profile", $user_id ) ) { - continue; - } + // Save the fields. + $group->save_fields( + array( + 'user_id' => $user_id, + 'scope' => 'checkout', + ) + ); + } +} +add_action( 'pmpro_checkout_before_user_auth', 'pmpro_checkout_before_user_auth_save_fields' ); - if(!empty($field->profile) && ($field->profile === "only" || $field->profile === "only_admin")) { - continue; //wasn't shown at checkout - } +/** + * Require required fields before creating a user at checkout. + */ +function pmpro_checkout_user_creation_checks_user_fields( $okay ) { + // Arrays to store fields that were required and missed. + $required = array(); + $required_labels = array(); - //assume no value - $value = NULL; + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + $user_creation_field_groups = pmpro_get_user_creation_field_groups(); + foreach($field_groups as $group_name => $group) { + if ( ! in_array( $group_name, $user_creation_field_groups ) ) { + continue; + } - // Where are we getting the value from? We sanitize $value right after this. - // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if(isset($_REQUEST[$field->name])) - { - //request - $value = $_REQUEST[$field->name]; - } - elseif(isset($_REQUEST[$field->name . '_checkbox']) && $field->type == 'checkbox') - { - //unchecked checkbox - $value = 0; - } - elseif(!empty($_POST[$field->name . "_checkbox"]) && in_array( $field->type, array( 'checkbox', 'checkbox_grouped', 'select2' ) ) ) //handle unchecked checkboxes - { - //unchecked checkbox - $value = array(); + // Loop through all the fields in the group. + $fields = $group->get_fields_to_display( + array( + 'scope' => 'checkout', + ) + ); + foreach($fields as $field) { + // If this is a file upload, check whether the file is allowed. + if ( isset( $_FILES[ $field->name ] ) && ! empty( $_FILES[$field->name]['name'] ) ) { + $upload_check = pmpro_check_upload( $field->name ); + if ( is_wp_error( $upload_check ) ) { + pmpro_setMessage( $upload_check->get_error_message(), 'pmpro_error' ); + return false; } - elseif(isset($_SESSION[$field->name])) - { - //file or value? - if(is_array($_SESSION[$field->name]) && isset($_SESSION[$field->name]['name'])) - { - //add to files global - $_FILES[$field->name] = $_SESSION[$field->name]; - - //set value to name - $value = $_SESSION[$field->name]['name']; - } - else - { - //session - $value = $_SESSION[$field->name]; - } + } - //unset - unset($_SESSION[$field->name]); - } - elseif(isset($_FILES[$field->name])) - { - //file - $value = $_FILES[$field->name]['name']; - } - // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // If the field was filled if needed, skip it. + if ( $field->was_filled_if_needed() ) { + continue; + } - //update user meta - if(isset($value)) - { - if ( ! empty( $field->sanitize ) ) { - $value = pmpro_sanitize( $value, $field ); - } + // The field was not filled. + $required[] = $field->name; + $required_labels[] = $field->label; + } + } - //callback? - if(!empty($field->save_function)) - call_user_func( $field->save_function, $user_id, $field->name, $value, $order ); - else - update_user_meta($user_id, $field->meta_key, $value); - } - } + if(!empty($required)) + { + $required = array_unique($required); + + //add them to error fields + global $pmpro_error_fields; + $pmpro_error_fields = array_merge((array)$pmpro_error_fields, $required); + + if( count( $required ) == 1 ) { + $pmpro_msg = sprintf( __( 'The %s field is required.', 'paid-memberships-pro' ), implode(", ", $required_labels) ); + $pmpro_msgt = 'pmpro_error'; + } else { + $pmpro_msg = sprintf( __( 'The %s fields are required.', 'paid-memberships-pro' ), implode(", ", $required_labels) ); + $pmpro_msgt = 'pmpro_error'; } + + if($okay) + pmpro_setMessage($pmpro_msg, $pmpro_msgt); + + return false; + } + + //return whatever status was before + return $okay; +} +add_filter( 'pmpro_checkout_user_creation_checks', 'pmpro_checkout_user_creation_checks_user_fields' ); + +/** + * Update the fields after a checkout is completed. + * + * @param int $user_id The ID of the user that was created. + * @param object $order The order object. + */ +function pmpro_after_checkout_save_fields( $user_id, $order ) { + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + $user_creation_field_groups = pmpro_get_user_creation_field_groups(); + foreach($field_groups as $group_name => $group) { + if ( in_array( $group_name, $user_creation_field_groups ) ) { + continue; + } + + // Save the fields. + $group->save_fields( + array( + 'user_id' => $user_id, + 'scope' => 'checkout', + ) + ); } } add_action( 'pmpro_after_checkout', 'pmpro_after_checkout_save_fields', 10, 2 ); @@ -472,55 +459,45 @@ function pmpro_after_checkout_save_fields( $user_id, $order ) { add_action( 'pmpro_before_send_to_payfast', 'pmpro_after_checkout_save_fields', 20, 2 ); //for the Payfast Gateway Add On /** - * Require required fields. + * Require required fields before creating an order at checkout. */ function pmpro_registration_checks_for_user_fields( $okay ) { - global $current_user; - - //arrays to store fields that were required and missed + // Arrays to store fields that were required and missed. $required = array(); $required_labels = array(); - //any fields? - global $pmpro_user_fields; - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) - { - //cycle through fields - foreach($fields as $field) - { - //handle arrays - $field->name = preg_replace('/\[\]$/', '', $field->name); - - //if the field is not for this level, skip it - if( ! pmpro_is_field( $field ) ) { - continue; - } - - if ( ! pmpro_check_field_for_level( $field ) ) { - continue; - } - - if(!empty($field->profile) && ($field->profile === "only" || $field->profile === "only_admin")) { - continue; //wasn't shown at checkout - } + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + $user_creation_field_groups = pmpro_get_user_creation_field_groups(); + foreach($field_groups as $group_name => $group) { + if ( in_array( $group_name, $user_creation_field_groups ) ) { + continue; + } - // If this is a file upload, check whether the file is allowed. - if ( isset( $_FILES[ $field->name ] ) && ! empty( $_FILES[$field->name]['name'] ) ) { - $upload_check = pmpro_check_upload( $field->name ); - if ( is_wp_error( $upload_check ) ) { - pmpro_setMessage( $upload_check->get_error_message(), 'pmpro_error' ); - return false; - } + // Loop through all the fields in the group. + $fields = $group->get_fields_to_display( + array( + 'scope' => 'checkout', + ) + ); + foreach($fields as $field) { + // If this is a file upload, check whether the file is allowed. + if ( isset( $_FILES[ $field->name ] ) && ! empty( $_FILES[$field->name]['name'] ) ) { + $upload_check = pmpro_check_upload( $field->name ); + if ( is_wp_error( $upload_check ) ) { + pmpro_setMessage( $upload_check->get_error_message(), 'pmpro_error' ); + return false; } + } - if( ! $field->was_filled_if_needed() ) { - $required[] = $field->name; - $required_labels[] = $field->label; - } + // If the field was filled if needed, skip it. + if ( $field->was_filled_if_needed() ) { + continue; } + + // The field was not filled. + $required[] = $field->name; + $required_labels[] = $field->label; } } @@ -557,68 +534,63 @@ function pmpro_registration_checks_for_user_fields( $okay ) { * @deprecated 2.12.4 Use pmpro_after_checkout_save_fields instead to save fields immediately or pmpro_save_checkout_data_to_order for delayed checkouts. */ function pmpro_paypalexpress_session_vars_for_user_fields() { - global $pmpro_user_fields; - _deprecated_function( __FUNCTION__, '2.12.4', 'pmpro_after_checkout_save_fields' ); - //save our added fields in session while the user goes off to PayPal - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Loop through all the fields in the group. + $fields = $group->get_fields(); + foreach($fields as $field) { - //cycle through fields - foreach($fields as $field) - { - if( ! pmpro_is_field( $field ) ) { - continue; - } - - if ( ! pmpro_check_field_for_level( $field ) ) { - continue; - } + if( ! pmpro_is_field( $field ) ) { + continue; + } + + if ( ! pmpro_check_field_for_level( $field ) ) { + continue; + } - if( isset( $_REQUEST[$field->name] ) ) { - $_SESSION[$field->name] = pmpro_sanitize( $_REQUEST[$field->name], $field ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - } elseif ( isset( $_FILES[$field->name] ) ) { - /* - We need to save the file somewhere and save values in $_SESSION - */ - // Make sure the file is allowed. - $upload_check = pmpro_check_upload( $field->name ); - if ( is_wp_error( $upload_check ) ) { - continue; - } + if( isset( $_REQUEST[$field->name] ) ) { + $_SESSION[$field->name] = pmpro_sanitize( $_REQUEST[$field->name], $field ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + } elseif ( isset( $_FILES[$field->name] ) ) { + /* + We need to save the file somewhere and save values in $_SESSION + */ + // Make sure the file is allowed. + $upload_check = pmpro_check_upload( $field->name ); + if ( is_wp_error( $upload_check ) ) { + continue; + } - // Get $file and $filetype. - $file = array_map( 'sanitize_text_field', $_FILES[ $field->name ] ); - $filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] ); + // Get $file and $filetype. + $file = array_map( 'sanitize_text_field', $_FILES[ $field->name ] ); + $filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] ); - // Make sure file was uploaded during this page load. - if ( ! is_uploaded_file( sanitize_text_field( $file['tmp_name'] ) ) ) { - continue; - } + // Make sure file was uploaded during this page load. + if ( ! is_uploaded_file( sanitize_text_field( $file['tmp_name'] ) ) ) { + continue; + } - //check for a register helper directory in wp-content - $upload_dir = wp_upload_dir(); - $pmprorh_dir = $upload_dir['basedir'] . "/pmpro-register-helper/tmp/"; + //check for a register helper directory in wp-content + $upload_dir = wp_upload_dir(); + $pmprorh_dir = $upload_dir['basedir'] . "/pmpro-register-helper/tmp/"; - //create the dir and subdir if needed - if(!is_dir($pmprorh_dir)) - { - wp_mkdir_p($pmprorh_dir); - } + //create the dir and subdir if needed + if(!is_dir($pmprorh_dir)) + { + wp_mkdir_p($pmprorh_dir); + } - //move file - $new_filename = $pmprorh_dir . basename( sanitize_file_name( $file['name'] ) ); - move_uploaded_file( sanitize_text_field( $$file['tmp_name'] ), $new_filename ); + //move file + $new_filename = $pmprorh_dir . basename( sanitize_file_name( $file['name'] ) ); + move_uploaded_file( sanitize_text_field( $$file['tmp_name'] ), $new_filename ); - //update location of file - $_FILES[$field->name]['tmp_name'] = $new_filename; + //update location of file + $_FILES[$field->name]['tmp_name'] = $new_filename; - //save file info in session - $_SESSION[$field->name] = array_map( 'sanitize_text_field', $file ); - } + //save file info in session + $_SESSION[$field->name] = array_map( 'sanitize_text_field', $file ); } } } @@ -626,65 +598,41 @@ function pmpro_paypalexpress_session_vars_for_user_fields() { /** * Show user fields in profile. + * + * @deprecated TBD */ function pmpro_show_user_fields_in_profile( $user, $withlocations = false ) { - global $pmpro_user_fields; - - //which fields are marked for the profile - $profile_fields = pmpro_get_user_fields_for_profile($user->ID, $withlocations); - - //show the fields - if(!empty($profile_fields) && $withlocations) - { - foreach($profile_fields as $where => $fields) - { - $box = pmpro_get_field_group_by_name($where); - - if ( !empty($box->label) ) { - ?> -

label ); ?>

- description ) ) { - ?> -

description ); ?>

- - - - - displayInProfile($user->ID); - } - ?> -
- - - displayInProfile($user->ID); - } - } - ?> -
- display( + array( + 'markup' => 'table', + 'scope' => 'profile', + 'show_group_label' => $withlocations, + 'user_id' => $user->ID, + ) + ); + } } + +/** + * Show user fields in the backend profile. + */ function pmpro_show_user_fields_in_profile_with_locations( $user ) { - pmpro_show_user_fields_in_profile($user, true); + $groups = PMPro_Field_Group::get_all(); + foreach( $groups as $group ) { + $group->display( + array( + 'markup' => 'table', + 'scope' => 'profile', + 'user_id' => $user->ID, + ) + ); + } } add_action( 'show_user_profile', 'pmpro_show_user_fields_in_profile_with_locations' ); add_action( 'edit_user_profile', 'pmpro_show_user_fields_in_profile_with_locations' ); @@ -693,69 +641,43 @@ function pmpro_show_user_fields_in_profile_with_locations( $user ) { * Show Profile fields on the frontend "Member Profile Edit" page. * * @since 2.3 + * @deprecated TBD */ function pmpro_show_user_fields_in_frontend_profile( $user, $withlocations = false ) { - //which fields are marked for the profile - $profile_fields = pmpro_get_user_fields_for_profile($user->ID, $withlocations); - - //show the fields - if ( ! empty( $profile_fields ) && $withlocations ) { - foreach( $profile_fields as $where => $fields ) { - $box = pmpro_get_field_group_by_name( $where ); - - // Only show on front-end if there are fields to be shown. - $show_fields = array(); - foreach( $fields as $key => $field ) { - if ( pmpro_is_field( $field ) && $field->profile !== 'only_admin' && $field->profile !== 'admin' && $field->profile !== 'admins' ) { - $show_fields[] = $field; - } - } - - // Bail if there are no fields to show on the front-end profile. - if ( empty( $show_fields ) ) { - continue; - } - ?> -
-
- label ) ) { ?> - -

label ); ?>

-
- -
- description ) ) { ?> -
description ); ?>
- - - displayAtCheckout( $user->ID ); - } - ?> -
-
- -
-
- profile !== 'only_admin' ) { - $field->displayAtCheckout( $user->ID ); - } - } - ?> -
-
- display( + array( + 'markup' => 'div', + 'scope' => 'profile', + 'show_group_label' => $withlocations, + 'user_id' => $user->ID, + ) + ); + } } + +/** + * Show Profile fields on the frontend "Member Profile Edit" page. + * + * @since 2.3 + */ function pmpro_show_user_fields_in_frontend_profile_with_locations( $user ) { - pmpro_show_user_fields_in_frontend_profile( $user, true ); + $groups = PMPro_Field_Group::get_all(); + foreach( $groups as $group ) { + $group->display( + array( + 'markup' => 'div', + 'scope' => 'profile', + 'user_id' => $user->ID, + ) + ); + } } add_action( 'pmpro_show_user_profile', 'pmpro_show_user_fields_in_frontend_profile_with_locations' ); @@ -764,47 +686,63 @@ function pmpro_show_user_fields_in_frontend_profile_with_locations( $user ) { * when using the Add Member Admin Add On. */ // Add fields to form. -function pmpro_add_member_admin_fields( $user = null, $user_id = null) -{ - global $pmpro_user_fields; - - $addmember_fields = array(); - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) - { - //cycle through fields - foreach($fields as $field) - { - if(pmpro_is_field($field) && isset($field->addmember) && !empty($field->addmember) && ( in_array( strtolower( $field->addmember ), array( 'true', 'yes' ) ) || true == $field->addmember ) ) - { - $addmember_fields[] = $field; - } - } - } +function pmpro_add_member_admin_fields( $user = null, $user_id = null) { + $addmember_fields = array(); + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Loop through all the fields in the group. + $fields = $group->get_fields(); + foreach($fields as $field) + { + if(pmpro_is_field($field) && isset($field->addmember) && !empty($field->addmember) && ( in_array( strtolower( $field->addmember ), array( 'true', 'yes' ) ) || true == $field->addmember ) ) + { + $addmember_fields[] = $field; + } + } } //show the fields - if(!empty($addmember_fields)) - { - ?> - ID)) { - $user_id = $user->ID; - } + if(!empty($addmember_fields)) { + //cycle through groups + foreach($addmember_fields as $field) + { + if(empty($user_id) && !empty($user) && !empty($user->ID)) { + $user_id = $user->ID; + } - if( pmpro_is_field( $field ) ) { - $field->displayInProfile($user_id); - } - - } - ?> - meta_key)) + { + $value = get_user_meta($user_id, $field->meta_key, true); + } else { + $value = ""; + } + ?> + + + showmainlabel ) ) { ?> + + + + + display($value); + else + echo "
" . wp_kses_post( $field->displayValue($value) ) . "
"; + ?> + hint)) { ?> +

hint );?>

+ + + + $fields) - { - //cycle through fields - foreach($fields as $field) - { - if(pmpro_is_field($field) && isset($field->addmember) && !empty($field->addmember) && ( in_array( strtolower( $field->addmember ), array( 'true', 'yes' ) ) || true == $field->addmember ) ) - { - $addmember_fields[] = $field; - } - } - } + $addmember_fields = array(); + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Loop through all the fields in the group. + $fields = $group->get_fields(); + foreach($fields as $field) + { + if(pmpro_is_field($field) && isset($field->addmember) && !empty($field->addmember) && ( in_array( strtolower( $field->addmember ), array( 'true', 'yes' ) ) || true == $field->addmember ) ) + { + $addmember_fields[] = $field; + } + } } //save our added fields in session while the user goes off to PayPal @@ -861,41 +796,7 @@ function pmpro_add_member_admin_save_user_fields( $uid = null, $user = null ) { //cycle through fields foreach($addmember_fields as $field) { - if(pmpro_is_field($field) && isset($_POST[$field->name]) || isset($_FILES[$field->name])) - { - // Sanitize by default, or not. Some fields may have custom save functions/etc. - // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( ! empty( $field->sanitize ) && isset( $_POST[ $field->name ] ) ) { - $value = pmpro_sanitize( $_POST[ $field->name ], $field ); - } elseif( isset($_POST[$field->name]) ) { - $value = $_POST[ $field->name ]; - } else { - $value = $_FILES[$field->name]; - } - // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, $value ); - else - update_user_meta($user_id, $field->meta_key, $value ); - } - elseif(pmpro_is_field($field) && !empty($_POST[$field->name . "_checkbox"]) && $field->type == 'checkbox') //handle unchecked checkboxes - { - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, 0); - else - update_user_meta($user_id, $field->meta_key, 0); - } - elseif(!empty($_POST[$field->name . "_checkbox"]) && in_array( $field->type, array( 'checkbox', 'checkbox_grouped', 'select2' ) ) ) //handle unchecked checkboxes - { - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, array()); - else - update_user_meta($user_id, $field->meta_key, array()); - } + $field->save_field_for_user( $user_id ); } } } @@ -905,22 +806,17 @@ function pmpro_add_member_admin_save_user_fields( $uid = null, $user = null ) { * Get user fields which are set to show up in the Members List CSV Export. */ function pmpro_get_user_fields_for_csv() { - global $pmpro_user_fields; - $csv_fields = array(); - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Loop through all the fields in the group. + $fields = $group->get_fields(); + foreach($fields as $field) { - //cycle through fields - foreach($fields as $field) + if(pmpro_is_field($field) && !empty($field->memberslistcsv) && ($field->memberslistcsv == "true")) { - if(pmpro_is_field($field) && !empty($field->memberslistcsv) && ($field->memberslistcsv == "true")) - { - $csv_fields[] = $field; - } - + $csv_fields[] = $field; } } } @@ -931,45 +827,31 @@ function pmpro_get_user_fields_for_csv() { /** * Get user fields which are marked to show in the profile. * If a $user_id is passed in, get fields based on the user's level. + * + * @deprecated TBD Use PMPro_Field_Group::get_fields_to_display instead. */ function pmpro_get_user_fields_for_profile( $user_id, $withlocations = false ) { - global $pmpro_user_fields; - + _deprecated_function( __FUNCTION__, 'TBD', 'PMPro_Field_Group::get_fields_to_display' ); $profile_fields = array(); - if(!empty($pmpro_user_fields)) - { - //cycle through groups - foreach($pmpro_user_fields as $where => $fields) - { - //cycle through fields - foreach($fields as $field) - { - if( ! pmpro_is_field( $field ) ) { - continue; - } - - if ( ! pmpro_check_field_for_level( $field, "profile", $user_id ) ) { - continue; - } + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Get the fields to display. + $fields_to_display = $group->get_fields_to_display( + array( + 'scope' => 'profile', + 'user_id' => $user_id, + ) + ); - if(!empty($field->profile) && ($field->profile === "admins" || $field->profile === "admin" || $field->profile === "only_admin")) - { - if( current_user_can( 'manage_options' ) || current_user_can( 'pmpro_membership_manager' ) ) - { - if($withlocations) - $profile_fields[$where][] = $field; - else - $profile_fields[] = $field; - } - } - elseif(!empty($field->profile)) - { - if($withlocations) - $profile_fields[$where][] = $field; - else - $profile_fields[] = $field; - } - } + if ( empty( $fields_to_display ) ) { + continue; + } + + if ( $withlocations ) { + $profile_fields[ $group_name ] = $fields_to_display; + } else { + $profile_fields = array_merge( $profile_fields, $fields_to_display ); } } @@ -992,54 +874,16 @@ function pmpro_save_user_fields_in_profile( $user_id ) if ( !current_user_can( 'edit_user', $user_id ) ) return false; - $profile_fields = pmpro_get_user_fields_for_profile($user_id); - - //save our added fields in session while the user goes off to PayPal - if(!empty($profile_fields)) - { - //cycle through fields - foreach($profile_fields as $field) - { - if( ! pmpro_is_field( $field ) ) { - continue; - } - - if(isset($_POST[$field->name]) || isset($_FILES[$field->name])) - { - // Sanitize by default, or not. Some fields may have custom save functions/etc. - // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - if ( ! empty( $field->sanitize ) && isset( $_POST[ $field->name ] ) ) { - $value = pmpro_sanitize( $_POST[ $field->name ], $field ); - } elseif( isset($_POST[$field->name]) ) { - $value = $_POST[ $field->name ]; - } else { - $value = $_FILES[$field->name]; - } - // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, $value); - else - update_user_meta($user_id, $field->meta_key, $value); - } - elseif(!empty($_POST[$field->name . "_checkbox"]) && $field->type == 'checkbox') //handle unchecked checkboxes - { - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, 0); - else - update_user_meta($user_id, $field->meta_key, 0); - } - elseif(!empty($_POST[$field->name . "_checkbox"]) && in_array( $field->type, array( 'checkbox', 'checkbox_grouped', 'select2' ) ) ) //handle unchecked checkboxes - { - //callback? - if(!empty($field->save_function)) - call_user_func($field->save_function, $user_id, $field->name, array()); - else - update_user_meta($user_id, $field->meta_key, array()); - } - } + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Save the fields. + $group->save_fields( + array( + 'scope' => 'profile', + 'user_id' => $user_id, + ) + ); } } add_action( 'personal_options_update', 'pmpro_save_user_fields_in_profile' ); @@ -1050,63 +894,31 @@ function pmpro_save_user_fields_in_profile( $user_id ) * Add user fields to confirmation email. */ function pmpro_add_user_fields_to_email( $email ) { - global $wpdb, $pmpro_user_fields, $pmpro_field_groups; + global $wpdb; //only update admin confirmation emails if ( ! empty( $email ) && strpos( $email->template, "checkout" ) !== false && strpos( $email->template, "admin" ) !== false ) { //get the user_id from the email $user_id = $wpdb->get_var( "SELECT ID FROM $wpdb->users WHERE user_email = '" . esc_sql( $email->data['user_email'] ) . "' LIMIT 1" ); - $level_id = empty( $email->data['membership_id'] ) ? null : intval( $email->data['membership_id'] ); if ( ! empty( $user_id ) ) { - - //add to bottom of email - if ( ! empty( $pmpro_field_groups ) ) { + $field_groups = PMPro_Field_Group::get_all(); + if ( ! empty( $field_groups ) ) { $fields_content = "

" . __( 'Extra Fields:', 'paid-memberships-pro' ) . "
"; $added_field = false; - //cycle through groups - foreach( $pmpro_field_groups as $group ) { - - // Get the groups name so we can grab it from the associative array. - $group_name = $group->name; - - // Skip if there are no fields in this group. - if ( empty( $pmpro_user_fields[$group_name] ) ) { - continue; - } - - //cycle through groups and fields associated with that group. - foreach( $pmpro_user_fields[$group_name] as $field ) { - - if ( ! pmpro_is_field( $field ) ) { - continue; - } - - // If the field is showing only in the profile or to admins we can skip it. - if ( ! empty( $field->profile ) && ( $field->profile === "only" || $field->profile === "only_admin" ) ) { - continue; - } - - // Let's make sure the field level ID's are the same as the one they checked out for. - if ( ! empty( $field->levels ) && ( empty( $level_id) || ! in_array( $level_id, $field->levels ) ) ) { - continue; - } - + // Loop through all the field groups. + foreach( $field_groups as $group_name => $group ) { + // Loop through all the fields in the group. + $fields = $group->get_fields_to_display( + array( + 'scope' => 'checkout', + 'user_id' => $user_id, + ) + ); + foreach( $fields as $field ) { $fields_content .= "- " . esc_html( $field->label ) . ": "; - $value = get_user_meta( $user_id, $field->name, true); - - // Get the label value for field types that have labels. - $value = pmpro_get_label_for_user_field_value( $field->name, $value ); - - if ( $field->type == "file" && is_array( $value ) && ! empty( $value['fullurl'] ) ) { - $fields_content .= pmpro_sanitize( $value['fullurl'], $field ); - } elseif( is_array( $value ) ) { - $fields_content .= implode(", ", pmpro_sanitize( $value, $field ) ); - } else { - $fields_content .= pmpro_sanitize( $value, $field ); - } - + $fields_content .= $field->displayValue( get_user_meta( $user_id, $field->name, true), false ); $fields_content .= "
"; $added_field = true; } @@ -1157,37 +969,14 @@ function pmpro_csv_columns_for_user_fields( $user, $column ) { } } -/** - * Delete old files in wp-content/uploads/pmpro-register-helper/tmp every day. - */ -function pmpro_cron_delete_tmp() { - $upload_dir = wp_upload_dir(); - $pmprorh_dir = $upload_dir['basedir'] . "/paid-memberships-pro/tmp/"; - - if(file_exists($pmprorh_dir) && $handle = opendir($pmprorh_dir)) - { - while(false !== ($file = readdir($handle))) - { - $file = $pmprorh_dir . $file; - $filelastmodified = filemtime($file); - if(is_file($file) && (time() - $filelastmodified) > 3600) - { - unlink($file); - } - } - - closedir($handle); - } - - exit; -} -add_action( 'pmpro_cron_delete_tmp', 'pmpro_cron_delete_tmp' ); - /** * Get user fields from global. * @since 2.9.3 + * @deprecated TBD */ function pmpro_get_user_fields() { + _deprecated_function( __FUNCTION__, 'TBD' ); + global $pmpro_user_fields; return (array)$pmpro_user_fields; @@ -1196,351 +985,20 @@ function pmpro_get_user_fields() { // Code for the user fields settings page. /** * Get field group HTML for settings. + * + * @deprecated TBD */ function pmpro_get_field_group_html( $group = null ) { - if ( ! empty( $group ) ) { - // Assume group stdClass in format we save to settings. - $group_name = $group->name; - $group_show_checkout = $group->checkout; - $group_show_profile = $group->profile; - $group_description = $group->description; - $group_levels = $group->levels; - $group_fields = $group->fields; - } else { - // Default group settings. - $group_name = ''; - $group_show_checkout = 'yes'; - $group_show_profile = 'yes'; - $group_description = ''; - $group_levels = array(); - $group_fields = array(); - } - - // Other vars - $levels = pmpro_sort_levels_by_order( pmpro_getAllLevels( true, true ) ); - - // Render field group HTML. - ?> -

-
-
- - - - - -
-

- -

- - -
- -
-
- -
- -
- -
- -
- -
- -
- -
-
-
3 ) { ?>style="height: 90px; overflow: auto;"> - -
- -
- -
-
- -
- -

- -
    -
  • -
  • -
  • -
  • -
- -
- fields ) ) { - foreach ( $group->fields as $field ) { - pmpro_get_field_html( $field ); - } - } - ?> - - - -
- -
- - -
- -
-
- label; - $field_name = $field->name; - $field_type = $field->type; - $field_required = $field->required; - $field_readonly = $field->readonly; - $field_profile = $field->profile; - $field_wrapper_class = $field->wrapper_class; - $field_element_class = $field->element_class; - $field_hint = $field->hint; - $field_options = $field->options; - $field_allowed_file_types = $field->allowed_file_types; - $field_max_file_size = $field->max_file_size; - $field_default = $field->default; - } else { - // Default field values - $field_label = ''; - $field_name = ''; - $field_type = ''; - $field_required = false; - $field_readonly = false; - $field_profile = ''; - $field_wrapper_class = ''; - $field_element_class = ''; - $field_hint = ''; - $field_options = ''; - $field_allowed_file_types = ''; - $field_max_file_size = ''; - $field_default = ''; - } - - // Other vars - $levels = pmpro_sort_levels_by_order( pmpro_getAllLevels( true, true ) ); - ?> -
- - - -
- __( 'More Information', 'paid-memberships-pro' ), + 'name' => 'more_information', + 'label' => __( 'More Information', 'paid-memberships-pro' ), 'checkout' => 'yes', 'profile' => 'yes', 'description' => '', @@ -1565,6 +1024,7 @@ function pmpro_get_user_fields_settings() { // Make sure all expected properties are set for each group. foreach ( $settings as $group ) { $group->name = ! empty( $group->name ) ? $group->name : ''; + $group->label = ! empty( $group->label ) ? $group->label : ''; $group->checkout = ! empty( $group->checkout ) ? $group->checkout : 'yes'; $group->profile = ! empty( $group->profile ) ? $group->profile : 'yes'; $group->description = ! empty( $group->description ) ? $group->description : ''; @@ -1596,11 +1056,10 @@ function pmpro_get_user_fields_settings() { * Load user field settings into the fields global var. */ function pmpro_load_user_fields_from_settings() { - global $pmpro_user_fields, $pmpro_field_groups; $settings_groups = pmpro_get_user_fields_settings(); foreach ( $settings_groups as $group ) { - pmpro_add_field_group( $group->name, $group->name, $group->description ); + $group_obj = PMPro_field_Group::add( $group->name, $group->label, $group->description ); // Figure out profile value. Change 2 settings values into 1 field value. if ( $group->checkout === 'yes' ) { @@ -1678,7 +1137,7 @@ function pmpro_load_user_fields_from_settings() { 'default' => $settings_field->default, ) ); - pmpro_add_user_field( $group->name, $field ); + $group_obj->add_field( $field ); } } } @@ -1688,11 +1147,13 @@ function pmpro_load_user_fields_from_settings() { * Check if user is adding custom user fields with code. * * @since 2.9 + * @deprecated TBD * * @return bool True if user is adding custom user fields with code. */ function pmpro_has_coded_user_fields() { - global $pmpro_user_fields, $pmprorh_registration_fields; + _deprecated_function( __FUNCTION__, 'TBD' ); + global $pmprorh_registration_fields; // Check if coded fields are being added using the PMPro Register Helper Add On active. if ( ! empty( $pmprorh_registration_fields ) ) { @@ -1700,15 +1161,16 @@ function pmpro_has_coded_user_fields() { } // Check if coded fields are being added using the PMPro Register Helper Add On inactive. - $num_db_fields = array_sum( array_map( function ($group) { return count( $group->fields ); }, pmpro_get_user_fields_settings() ) ); // Fields from UI settings page. - $num_global_fields = array_sum( array_map( 'count', $pmpro_user_fields ) ); // Total loaded fields. - return $num_global_fields > $num_db_fields; + $num_fields_from_settings = array_sum( array_map( function ($group) { return count( $group->fields ); }, pmpro_get_user_fields_settings() ) ); // Fields from UI settings page. + $total_registered_fields = array_sum( array_map( function ($group) { return count( $group->get_fields() ); }, PMPro_Field_Group::get_all() ) ); // All registered fields. + return $total_registered_fields > $num_fields_from_settings; } /** * Gets the label(s) for a passed user field value. * * @since 2.11 + * @deprecated TBD Use PMProField::displayValue instead. * * @param string $field_name The name of the field that the value belongs to. * @param string|array $field_value The value to get the label for. @@ -1716,9 +1178,14 @@ function pmpro_has_coded_user_fields() { * @return string|array The label(s) for the passed value. Will be same type as $field_value. */ function pmpro_get_label_for_user_field_value( $field_name, $field_value ) { - global $pmpro_user_fields; - foreach ( $pmpro_user_fields as $user_field_group ) { // Loop through each user field group. - foreach ( $user_field_group as $user_field ) { // Loop through each user field in the group. + _deprecated_function( __FUNCTION__, 'TBD', 'PMProField::displayValue' ); + + // Loop through all the field groups. + $field_groups = PMPro_Field_Group::get_all(); + foreach($field_groups as $group_name => $group) { + // Loop through all the fields in the group. + $fields = $group->get_fields(); + foreach( $fields as $user_field ) { // Check if this is the user field that we are displaying. if ( $user_field->name !== $field_name ) { continue; @@ -1740,42 +1207,21 @@ function pmpro_get_label_for_user_field_value( $field_name, $field_value ) { } // Replace meta values with their corresponding labels. - if ( is_array( $field_value ) ) { - foreach ( $field_value as $key => $value ) { - if ( isset( $user_field->options[ $value ] ) ) { - $field_value[ $key ] = $user_field->options[ $value ]; - } - } - } else { - if ( isset( $user_field->options[ $field_value ] ) ) { - $field_value = $user_field->options[ $field_value ]; - } - } + $field_value = $user_field->displayValue( $field_value, false ); } } return $field_value; } /** - * Get a single field from the global $pmpro_user_fields array. + * Get a single user field. * @since 3.0 + * @deprecated TBD * @param string $field_name The name of the field to get. * @return bool|object The field object if found, false otherwise. */ function pmpro_get_user_field( $field_name ) { - global $pmpro_user_fields; - - if ( empty( $pmpro_user_fields ) ) { - return false; - } - - foreach ( $pmpro_user_fields as $group ) { - foreach ( $group as $field ) { - if ( $field->name === $field_name ) { - return $field; - } - } - } - - return false; + _deprecated_function( __FUNCTION__, 'TBD', 'PMPro_Field_Group::get_field' ); + $field = PMPro_Field_Group::get_field( $field_name ); + return empty( $field ) ? false : $field; } diff --git a/includes/functions.php b/includes/functions.php index 1da6088ee..395710621 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -3809,12 +3809,17 @@ function pmpro_sanitize_with_safelist( $needle, $safelist ) { * Sanitizes the passed value. * Default sanitizing for things like user fields. * + * @since TBD Marking the $field argument as deprecated. + * * @param array|int|null|string|stdClass $value The value to sanitize - * @param PMPro_Field $field (optional) Field to check type. * * @return array|int|string|object Sanitized value */ function pmpro_sanitize( $value, $field = null ) { + if ( null !== $field ) { + // This argument is deprecated. User fields now have sanitization logic in the field class. + _deprecated_argument( __FUNCTION__, 'TBD', __( 'The $field argument is deprecated. The sanitization logic is now built into the PMPro_Field class.', 'paid-memberships-pro' ) ); + } if ( is_array( $value ) ) { @@ -4759,8 +4764,6 @@ function pmpro_set_expiration_date( $user_id, $level_id, $enddate ) { * @return true|WP_Error True if the file is allowed, otherwise a WP_Error object. */ function pmpro_check_upload( $file_index ) { - global $pmpro_user_fields; - // Check if the file was uploaded. if ( empty( $_FILES[ $file_index ] ) ) { return new WP_Error( 'pmpro_upload_error', __( 'No file was uploaded.', 'paid-memberships-pro' ) ); @@ -4781,51 +4784,40 @@ function pmpro_check_upload( $file_index ) { } // If this is an upload for a user field, we need to perform additional checks. - $is_user_field = false; - if ( ! empty( $pmpro_user_fields ) && is_array( $pmpro_user_fields ) ) { - foreach ( $pmpro_user_fields as $checkout_box ) { - foreach ( $checkout_box as $field ) { - if ( $field->name == $file_index ) { - // This file is being uploaded for a user field. - $is_user_field = true; - - // First, make sure that this is a 'file' field. - if ( $field->type !== 'file' ) { - return new WP_Error( 'pmpro_upload_error', __( 'Invalid field input.', 'paid-memberships-pro' ) ); - } + $field = PMPro_Field_Group::get_field( $file_index ); + if ( ! empty( $field) ) { + // First, make sure that this is a 'file' field. + if ( $field->type !== 'file' ) { + return new WP_Error( 'pmpro_upload_error', __( 'Invalid field input.', 'paid-memberships-pro' ) ); + } - // If there are allowed file types, check if the file is an allowed file type. - // It does not look like the ext property is documented anywhere, but keeping it in case sites are using it. - if ( ! empty( $field->ext ) && is_array( $field->ext ) && ! in_array( $filetype['ext'], $field->ext ) ) { - return new WP_Error( 'pmpro_upload_error', __( 'Invalid file type.', 'paid-memberships-pro' ) ); - } + // If there are allowed file types, check if the file is an allowed file type. + // It does not look like the ext property is documented anywhere, but keeping it in case sites are using it. + if ( ! empty( $field->ext ) && is_array( $field->ext ) && ! in_array( $filetype['ext'], $field->ext ) ) { + return new WP_Error( 'pmpro_upload_error', __( 'Invalid file type.', 'paid-memberships-pro' ) ); + } - // Check the file type against the allowed types. - $allowed_mime_types = ! empty( $field->allowed_file_types ) ? array_map( 'sanitize_text_field', explode( ',', $field->allowed_file_types ) ) : array(); + // Check the file type against the allowed types. + $allowed_mime_types = ! empty( $field->allowed_file_types ) ? array_map( 'sanitize_text_field', explode( ',', $field->allowed_file_types ) ) : array(); - //Remove fullstops from the beginning of the allowed file types. - $allowed_mime_types = array_map( function( $type ) { - return ltrim( $type, '.' ); - }, $allowed_mime_types ); + //Remove fullstops from the beginning of the allowed file types. + $allowed_mime_types = array_map( function( $type ) { + return ltrim( $type, '.' ); + }, $allowed_mime_types ); - // Check the file type against the allowed types. If empty allowed mimes, assume any file upload is okay. - if ( ! empty( $allowed_mime_types ) && ! in_array( $filetype['ext'], $allowed_mime_types ) ) { - return new WP_Error( 'pmpro_upload_file_type_error', sprintf( esc_html__( 'Invalid file type. Please try uploading the file type(s): %s', 'paid-memberships-pro' ), implode( ',' ,$allowed_mime_types ) ) ); - } - - // Check if the file upload is too big to upload. - if ( $field->max_file_size > 0 ) { - $upload_max_file_size_in_bytes = $field->max_file_size * 1024 * 1024; - if ( $file['size'] > $upload_max_file_size_in_bytes ) { - return new WP_Error( 'pmpro_upload_file_size_error', sprintf( esc_html__( 'File size is too large for %s. Please upload files smaller than %dMB.', 'paid-memberships-pro' ), $field->label, $field->max_file_size ) ); - } - } - } + // Check the file type against the allowed types. If empty allowed mimes, assume any file upload is okay. + if ( ! empty( $allowed_mime_types ) && ! in_array( $filetype['ext'], $allowed_mime_types ) ) { + return new WP_Error( 'pmpro_upload_file_type_error', sprintf( esc_html__( 'Invalid file type. Please try uploading the file type(s): %s', 'paid-memberships-pro' ), implode( ',' ,$allowed_mime_types ) ) ); + } + + // Check if the file upload is too big to upload. + if ( $field->max_file_size > 0 ) { + $upload_max_file_size_in_bytes = $field->max_file_size * 1024 * 1024; + if ( $file['size'] > $upload_max_file_size_in_bytes ) { + return new WP_Error( 'pmpro_upload_file_size_error', sprintf( esc_html__( 'File size is too large for %s. Please upload files smaller than %dMB.', 'paid-memberships-pro' ), $field->label, $field->max_file_size ) ); } } - } - - if ( ! $is_user_field ) { + } else { /** * Filter whether a file not associated with a user field can be uploaded. * diff --git a/includes/scripts.php b/includes/scripts.php index 050f88c4c..2098f2149 100644 --- a/includes/scripts.php +++ b/includes/scripts.php @@ -137,14 +137,6 @@ function pmpro_admin_enqueue_scripts() { $level->formatted_expiration = trim( pmpro_no_quotes( pmpro_getLevelExpiration( $level ) ) ); $all_levels_formatted_text[$level->id] = $level; } - // Get HTML for empty field group. - ob_start(); - pmpro_get_field_group_html(); - $empty_field_group_html = ob_get_clean(); - // Get HTML for empty field. - ob_start(); - pmpro_get_field_html(); - $empty_field_html = ob_get_clean(); wp_localize_script( 'pmpro_admin', @@ -154,8 +146,6 @@ function pmpro_admin_enqueue_scripts() { 'all_levels_formatted_text' => $all_levels_formatted_text, 'all_level_values_and_labels' => $all_level_values_and_labels, 'checkout_url' => pmpro_url( 'checkout' ), - 'user_fields_blank_group' => $empty_field_group_html, - 'user_fields_blank_field' => $empty_field_html, // We want the core WP translation so we can check for it in JS. 'plugin_updated_successfully_text' => __( 'Plugin updated successfully.' ), ) diff --git a/includes/services.php b/includes/services.php index d5045250a..8d9743c4d 100644 --- a/includes/services.php +++ b/includes/services.php @@ -197,18 +197,127 @@ function pmpro_update_level_group_order() { // User fields AJAX. /** * Callback to draw a field group. + * + * @deprecated TBD */ -function pmpro_userfields_get_group_ajax() { - pmpro_get_field_group_html(); +function pmpro_userfields_get_group_ajax() { + _deprecated_function( __FUNCTION__, 'TBD' ); exit; } add_action( 'wp_ajax_pmpro_userfields_get_group', 'pmpro_userfields_get_group_ajax' ); /** * Callback to draw a field. + * + * @deprecated TBD */ function pmpro_userfields_get_field_ajax() { - pmpro_get_field_html(); + _deprecated_function( __FUNCTION__, 'TBD' ); exit; } add_action( 'wp_ajax_pmpro_userfields_get_field', 'pmpro_userfields_get_field_ajax' ); + +function pmpro_update_field_order() { + // only admins can get this + if ( ! function_exists( 'current_user_can' ) || ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'pmpro_userfields' ) ) ) { + die( esc_html__( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) ); + } + + // Check the nonce. + if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['nonce'] ), 'pmpro_update_field_order' ) ) { + die( esc_html__( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) ); + } + + // Get the field group that was reordered and the new order. + $field_group = sanitize_text_field( $_REQUEST['group'] ); + $ordered_fields = array_map( 'sanitize_text_field', $_REQUEST['ordered_fields'] ); + + // Get the current user fields settings. + $current_settings = pmpro_get_user_fields_settings(); + + // Find the group object that we are reordering. + $group = null; + foreach ( $current_settings as $group_settings ) { + if ( $group_settings->name === $field_group ) { + $group = $group_settings; + break; + } + } + if ( empty( $group ) ) { + die( esc_html__( 'Could not find the group to reorder.', 'paid-memberships-pro' ) ); + } + + // Create an associative version of $group->fields to make it easier to reorder. + $group_field_tmp = array(); + foreach ( $group->fields as $field ) { + $group_field_tmp[ $field->name ] = $field; + } + + // Create a reordered version of the fields. + $reordered_fields = array(); + foreach ( $ordered_fields as $field_name ) { + if ( isset( $group_field_tmp[ $field_name ] ) ) { + $reordered_fields[] = $group_field_tmp[ $field_name ]; + unset( $group_field_tmp[ $field_name ] ); + } + } + + // If there are any fields left in $group_field_tmp, add them to the end of $reordered_fields. + if ( ! empty( $group_field_tmp ) ) { + $reordered_fields = array_merge( $reordered_fields, $group_field_tmp ); + } + + // Update the group with the reordered fields. + $group->fields = $reordered_fields; + + // Update the settings with the reordered group. + update_option( 'pmpro_user_fields_settings', $current_settings ); + + exit; +} +add_action('wp_ajax_pmpro_update_field_order', 'pmpro_update_field_order'); + + +function pmpro_update_field_group_order() { + // only admins can get this + if ( ! function_exists( 'current_user_can' ) || ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'pmpro_userfields' ) ) ) { + die( esc_html__( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) ); + } + + // Check the nonce. + if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['nonce'] ), 'pmpro_update_field_group_order' ) ) { + die( esc_html__( 'You do not have permissions to perform this action.', 'paid-memberships-pro' ) ); + } + + // Get the new order. + $ordered_groups = array_map( 'sanitize_text_field', $_REQUEST['ordered_groups'] ); + + // Get the current user fields settings. + $current_settings = pmpro_get_user_fields_settings(); + + // Create an associative version of $current_settings to make it easier to reorder. + $current_settings_tmp = array(); + foreach ( $current_settings as $group_settings ) { + $current_settings_tmp[ $group_settings->name ] = $group_settings; + } + + // Create a reordered version of the groups. + $reordered_groups = array(); + foreach ( $ordered_groups as $group_name ) { + if ( isset( $current_settings_tmp[ $group_name ] ) ) { + $reordered_groups[] = $current_settings_tmp[ $group_name ]; + unset( $current_settings_tmp[ $group_name ] ); + } + } + + // If there are any groups left in $current_settings_tmp, add them to the end of $reordered_groups. + if ( ! empty( $current_settings_tmp ) ) { + $reordered_groups = array_merge( $reordered_groups, $current_settings_tmp ); + } + + // Update the settings with the reordered groups. + update_option( 'pmpro_user_fields_settings', $reordered_groups ); + + exit; +} +add_action('wp_ajax_pmpro_update_field_group_order', 'pmpro_update_field_group_order'); diff --git a/js/pmpro-admin.js b/js/pmpro-admin.js index 16467385d..49619ecb3 100644 --- a/js/pmpro-admin.js +++ b/js/pmpro-admin.js @@ -257,315 +257,39 @@ jQuery(document).ready(function () { // Function to prep click events. function pmpro_userfields_prep_click_events() { - // Whenever we make a change, warn the user if they try to navigate away. - function pmpro_userfields_made_a_change() { - window.onbeforeunload = function () { - return true; - }; - jQuery('#pmpro_userfields_savesettings').prop("disabled", false); - } - - // Add group button. - jQuery('#pmpro_userfields_add_group').unbind('click').on('click', function (event) { - jQuery('#pmpro_userfields_add_group').parent('p').before(pmpro.user_fields_blank_group); - pmpro_userfields_prep_click_events(); - jQuery('#pmpro_userfields_add_group').parent('p').prev().find('input').focus().select(); - pmpro_userfields_made_a_change(); - }); - - // Delete group button. - jQuery('.pmpro_userfield-group-actions button[name=pmpro_userfields_delete_group]').unbind('click').on('click', function (event) { - var thegroup = jQuery(this).closest('.pmpro_userfield-group'); - var thename = thegroup.find('input[name=pmpro_userfields_group_name]').val(); - var answer; - if (thename.length > 0) { - answer = window.confirm('Delete the "' + thename + '" group?'); - } else { - answer = window.confirm('Delete this group?'); - } - if (answer) { - thegroup.remove(); - pmpro_userfields_made_a_change(); - } - }); + function update_userfield_type_fields() { + // Hide all elements with the `field_type` class. + jQuery('.field_type').hide(); - // Add field button. - jQuery('button[name="pmpro_userfields_add_field"]').unbind('click').on('click', function (event) { - var thefields = jQuery(event.target).closest('div.pmpro_userfield-group-actions').siblings('div.pmpro_userfield-group-fields'); - thefields.append(pmpro.user_fields_blank_field); - pmpro_userfields_prep_click_events(); - thefields.children().last().find('.edit-field').click(); - thefields.children().last().find('input[name="pmpro_userfields_field_label"]').focus().select(); - pmpro_userfields_made_a_change(); - }); + // Get the selected field type. + var field_type = jQuery('.pmpro_admin-pmpro-userfields select[name=type]').val(); - // Delete field button. - jQuery('.pmpro_userfield-field-options a.delete-field, .pmpro_userfield-field-actions .is-destructive').unbind('click').on('click', function (event) { - var thefield = jQuery(this).closest('.pmpro_userfield-group-field'); - var thelabel = thefield.find('input[name=pmpro_userfields_field_label]').val(); - var answer; - if (thelabel.length > 0) { - answer = window.confirm('Delete the "' + thelabel + '" field?'); - } else { - answer = window.confirm('Delete this unlabeled field?'); - } - if (answer) { - thefield.remove(); - pmpro_userfields_made_a_change(); - } - }); - - // Toggle groups. - jQuery('button.pmpro_userfield-group-buttons-button-toggle-group, div.pmpro_userfield-group-header h3').unbind('click').on('click', function (event) { - event.preventDefault(); - - // Ignore if the text field was clicked. - if (jQuery(event.target).prop('nodeName') === 'INPUT') { - return; - } - - // Find the toggle button and open or close. - let thebutton = jQuery(event.target).parents('.pmpro_userfield-group').find('button.pmpro_userfield-group-buttons-button-toggle-group'); - let buttonicon = thebutton.children('.dashicons'); - let groupheader = thebutton.closest('.pmpro_userfield-group-header'); - let groupinside = groupheader.siblings('.pmpro_userfield-inside'); - - if (buttonicon.hasClass('dashicons-arrow-up')) { - // closing - buttonicon.removeClass('dashicons-arrow-up'); - buttonicon.addClass('dashicons-arrow-down'); - groupinside.slideUp(); - } else { - // opening - buttonicon.removeClass('dashicons-arrow-down'); - buttonicon.addClass('dashicons-arrow-up'); - groupinside.slideDown(); - } - }); - - // Move group up. - jQuery('.pmpro_userfield-group-buttons-button-move-up').unbind('click').on('click', function (event) { - var thegroup = jQuery(this).closest('.pmpro_userfield-group'); - var thegroupprev = thegroup.prev('.pmpro_userfield-group'); - if (thegroupprev.length > 0) { - thegroup.insertBefore(thegroupprev); - pmpro_userfields_made_a_change(); - } - }); - - // Move group down. - jQuery('.pmpro_userfield-group-buttons-button-move-down').unbind('click').on('click', function (event) { - var thegroup = jQuery(this).closest('.pmpro_userfield-group'); - var thegroupnext = thegroup.next('.pmpro_userfield-group'); - if (thegroupnext.length > 0) { - thegroup.insertAfter(thegroupnext); - pmpro_userfields_made_a_change(); - } - }); - - // Open field. - jQuery('a.edit-field').unbind('click').on('click', function (event) { - var fieldcontainer = jQuery(this).parents('.pmpro_userfield-group-field'); - var fieldsettings = fieldcontainer.children('.pmpro_userfield-field-settings'); - - fieldcontainer.removeClass('pmpro_userfield-group-field-collapse'); - fieldcontainer.addClass('pmpro_userfield-group-field-expand'); - fieldsettings.find('select[name=pmpro_userfields_field_type]').change(); - fieldsettings.show(); - }); - - // Close field. - jQuery('button.pmpro_userfields_close_field').unbind('click').on('click', function (event) { - event.preventDefault(); - var fieldcontainer = jQuery(this).parents('.pmpro_userfield-group-field'); - var fieldsettings = fieldcontainer.children('.pmpro_userfield-field-settings'); - var fieldheading = fieldsettings.prev(); - // Update label, name, and type. - fieldheading.find('span.pmpro_userfield-label').html(fieldsettings.find('input[name=pmpro_userfields_field_label]').val().replace(/(<([^>]+)>)/gi, '')); - fieldheading.find('li.pmpro_userfield-group-column-name').html(fieldsettings.find('input[name=pmpro_userfields_field_name]').val()); - fieldheading.find('li.pmpro_userfield-group-column-type').html(fieldsettings.find('select[name=pmpro_userfields_field_type]').val()); - - // Toggle - fieldcontainer.removeClass('pmpro_userfield-group-field-expand'); - fieldcontainer.addClass('pmpro_userfield-group-field-collapse'); - fieldsettings.hide(); - }); - - // Move field up. - jQuery('.pmpro_userfield-field-buttons-button-move-up').unbind('click').on('click', function (event) { - var thefield = jQuery(this).closest('.pmpro_userfield-group-field'); - var thefieldprev = thefield.prev('.pmpro_userfield-group-field'); - if (thefieldprev.length > 0) { - thefield.insertBefore(thefieldprev); - pmpro_userfields_made_a_change(); - } - }); - - // Move field down. - jQuery('.pmpro_userfield-field-buttons-button-move-down').unbind('click').on('click', function (event) { - var thefield = jQuery(this).closest('.pmpro_userfield-group-field'); - var thefieldnext = thefield.next('.pmpro_userfield-group-field'); - if (thefieldnext.length > 0) { - thefield.insertAfter(thefieldnext); - pmpro_userfields_made_a_change(); - } - }); - - // Duplicate field. - jQuery('a.duplicate-field').unbind('click').on('click', function (event) { - var thefield = jQuery(this).closest('.pmpro_userfield-group-field'); - thefield.clone(true).insertAfter(thefield); // clone( true ) to clone event handlers. - pmpro_userfields_made_a_change(); - }); + // Show al the elements with `field_type_{field_type}` class. + jQuery('.field_type_' + field_type).show(); + } + update_userfield_type_fields(); // Toggle field settings based on type. - jQuery('select[name=pmpro_userfields_field_type]').on('change', function (event) { - var fieldcontainer = jQuery(this).parents('.pmpro_userfield-group-field'); - var fieldsettings = fieldcontainer.children('.pmpro_userfield-field-settings'); - var fieldtype = jQuery(this).val(); - - var fieldoptions = fieldsettings.find('textarea[name=pmpro_userfields_field_options]').parents('.pmpro_userfield-field-setting'); - var fieldfiles = fieldsettings.find('input[name=pmpro_userfields_field_max_file_size]').parents('.pmpro_userfield-field-setting'); - var fielddefault = fieldsettings.find('input[name=pmpro_userfields_field_default]').parents('.pmpro_userfield-field-setting'); - - // Hide all the field settings. - fieldoptions.hide(); - fieldfiles.hide(); - fielddefault.hide(); - - // Show the option field if needed. - var optiontypes = ['checkbox_grouped', 'radio', 'select', 'select2', 'multiselect']; - if (jQuery.inArray(fieldtype, optiontypes) > -1) { - fieldoptions.show(); - } - - // Show the file field options if needed. - if (fieldtype === 'file') { - fieldfiles.show(); - } - - // Show the default field if needed. - var defaulttypes = ['text', 'textarea', 'checkbox', 'radio', 'select', 'date', 'readonly', 'hidden', 'number']; - if (jQuery.inArray(fieldtype, defaulttypes) > -1) { - fielddefault.show(); - } + jQuery('.pmpro_admin-pmpro-userfields select[name=type]').on('change', function (event) { + update_userfield_type_fields(); }); // Suggest name after leaving label field. - jQuery('input[name=pmpro_userfields_field_label]').on('focusout', function (event) { - var fieldcontainer = jQuery(this).parents('.pmpro_userfield-group-field'); - var fieldsettings = fieldcontainer.children('.pmpro_userfield-field-settings'); - var fieldname = fieldsettings.find('input[name=pmpro_userfields_field_name]'); - if (!fieldname.val()) { - fieldname.val(jQuery(this).val().toLowerCase().replace(/[^a-z0-9]/gi, '_').replace(/(^\_+|\_+$)/mg, '')); + jQuery('.pmpro_admin-pmpro-userfields input[name=label]').on('focusout', function (event) { + // Check if the "name" field is empty and a text field. + var name = jQuery('.pmpro_admin-pmpro-userfields input[name=name]').val(); + var label = jQuery('.pmpro_admin-pmpro-userfields input[name=label]').val(); + if ( ! name && label ) { + // Generate a name based on the label. + name = label.toLowerCase().replace(/[^a-z0-9]/gi, '_').replace(/(^\_+|\_+$)/mg, ''); + jQuery('.pmpro_admin-pmpro-userfields input[name=name]').val(name); } }); - // If we change a field, mark it as changed. - jQuery('.pmpro_userfield-group input, .pmpro_userfield-group textarea, .pmpro_userfield-group select').on('change', function (event) { - pmpro_userfields_made_a_change(); - }); - - // Save User Field Settings - jQuery('#pmpro_userfields_savesettings').unbind('click').on('click', function (event) { - ///event.preventDefault(); - // We have saved, so we no longer need to warn user if they try to navigate away. - window.onbeforeunload = null; - - let field_groups = []; - let group_names = []; - let default_group_name = 'More Information'; - - jQuery('.pmpro_userfield-group').each(function (index, value) { - let group_name = jQuery(this).find('input[name=pmpro_userfields_group_name]').val(); - - // Make sure name is not blank. - if (group_name.length === 0) { - group_name = default_group_name; - } - // Make sure name is unique. - let count = 1; - while (group_names.includes(group_name)) { - count++; - group_name = group_name.replace(/\(0-9*\)/, ''); - group_name = group_name + ' (' + String(count) + ')'; - } - group_names.push(group_name); - - let group_checkout = jQuery(this).find('select[name=pmpro_userfields_group_checkout]').val(); - let group_profile = jQuery(this).find('select[name=pmpro_userfields_group_profile]').val(); - let group_description = jQuery(this).find('textarea[name=pmpro_userfields_group_description]').val(); - - // Get level ids. - let group_levels = []; - jQuery(this).find('input[name="pmpro_userfields_group_membership[]"]:checked').each(function () { - group_levels.push(parseInt(jQuery(this).attr('id').replace('pmpro_userfields_group_membership_', ''))); - }); - - // Get fields. - let group_fields = []; - jQuery(this).find('div.pmpro_userfield-group-fields div.pmpro_userfield-field-settings').each(function () { - let field_label = jQuery(this).find('input[name=pmpro_userfields_field_label]').val(); - let field_name = jQuery(this).find('input[name=pmpro_userfields_field_name]').val(); - let field_type = jQuery(this).find('select[name=pmpro_userfields_field_type]').val(); - let field_required = jQuery(this).find('select[name=pmpro_userfields_field_required]').val(); - let field_readonly = jQuery(this).find('select[name=pmpro_userfields_field_readonly]').val(); - let field_profile = jQuery(this).find('select[name=pmpro_userfields_field_profile]').val(); - let field_wrapper_class = jQuery(this).find('input[name=pmpro_userfields_field_class]').val(); - let field_element_class = jQuery(this).find('input[name=pmpro_userfields_field_divclass]').val(); - let field_hint = jQuery(this).find('textarea[name=pmpro_userfields_field_hint]').val(); - let field_options = jQuery(this).find('textarea[name=pmpro_userfields_field_options]').val(); - let field_allowed_file_types = jQuery(this).find('input[name=pmpro_userfields_field_allowed_file_types]').val(); - let field_max_file_size = jQuery(this).find('input[name=pmpro_userfields_field_max_file_size]').val(); - let field_default = jQuery(this).find('input[name=pmpro_userfields_field_default]').val(); - - // Get level ids. - let field_levels = []; - jQuery(this).find('input[name="pmpro_userfields_field_levels[]"]:checked').each(function () { - field_levels.push(parseInt(jQuery(this).attr('id').replace('pmpro_userfields_field_levels_', ''))); - }); - - let field = { - 'label': field_label, - 'name': field_name, - 'type': field_type, - 'required': field_required, - 'readonly': field_readonly, - 'levels': field_levels, - 'profile': field_profile, - 'wrapper_class': field_wrapper_class, - 'element_class': field_element_class, - 'hint': field_hint, - 'options': field_options, - 'allowed_file_types': field_allowed_file_types, - 'max_file_size': field_max_file_size, - 'default': field_default - }; - - // Add to array. (Only if it has a label or name.) - if (field.label.length > 0 || field.name.length > 0) { - group_fields.push(field); - } - }); - - // Set up the field group object. - let field_group = { - 'name': group_name, - 'checkout': group_checkout, - 'profile': group_profile, - 'description': group_description, - 'levels': group_levels, - 'fields': group_fields - }; - - // Add to array. - field_groups.push(field_group); - }); - - // console.log( field_groups ); - jQuery('#pmpro_user_fields_settings').val(JSON.stringify(field_groups)); - - return true; + jQuery('.pmpro-level-restrictions-preview-button').on('click', function(event) { + event.preventDefault(); + jQuery(this).hide(); + jQuery(this).next('.pmpro-level-restrictions-preview-list').show(); }); } diff --git a/paid-memberships-pro.php b/paid-memberships-pro.php index 38efe602f..59c5ba4a3 100644 --- a/paid-memberships-pro.php +++ b/paid-memberships-pro.php @@ -46,6 +46,7 @@ require_once( PMPRO_DIR . '/classes/class.memberorder.php' ); // class to process and save orders require_once( PMPRO_DIR . '/classes/class.pmproemail.php' ); // setup and filter emails sent by PMPro require_once( PMPRO_DIR . '/classes/class-pmpro-field.php' ); +require_once( PMPRO_DIR . '/classes/class-pmpro-field-group.php' ); require_once( PMPRO_DIR . '/classes/class-pmpro-levels.php' ); require_once( PMPRO_DIR . '/classes/class-pmpro-subscription.php' ); require_once( PMPRO_DIR . '/classes/class-pmpro-admin-activity-email.php' ); // setup the admin activity email diff --git a/scheduled/crons.php b/scheduled/crons.php index ff3720787..6457927c6 100644 --- a/scheduled/crons.php +++ b/scheduled/crons.php @@ -377,3 +377,29 @@ function pmpro_cron_recurring_payment_reminders() { $previous_days = $days; } } + +/** + * Delete old files in wp-content/uploads/pmpro-register-helper/tmp every day. + */ +function pmpro_cron_delete_tmp() { + $upload_dir = wp_upload_dir(); + $pmprorh_dir = $upload_dir['basedir'] . "/paid-memberships-pro/tmp/"; + + if(file_exists($pmprorh_dir) && $handle = opendir($pmprorh_dir)) + { + while(false !== ($file = readdir($handle))) + { + $file = $pmprorh_dir . $file; + $filelastmodified = filemtime($file); + if(is_file($file) && (time() - $filelastmodified) > 3600) + { + unlink($file); + } + } + + closedir($handle); + } + + exit; +} +add_action( 'pmpro_cron_delete_tmp', 'pmpro_cron_delete_tmp' ); diff --git a/shortcodes/pmpro_member.php b/shortcodes/pmpro_member.php index 07ce3be76..a9c9ea3f7 100644 --- a/shortcodes/pmpro_member.php +++ b/shortcodes/pmpro_member.php @@ -185,21 +185,12 @@ function pmpro_member_shortcode( $atts, $content = null, $shortcode_tag = '' ) { } } - // Check for files to reformat them. - if ( is_array( $r ) && ! empty( $r['fullurl'] ) ) { - $file_field = pmpro_get_user_field( $field ); - if ( ! empty( $file_field ) ) { - $file_field->file = $r; - $file_field->readonly = true; - $r = $file_field->displayValue( $r['fullurl'], false ); // False to not echo. - } else { - $r = '' . esc_html( basename($r['fullurl'] ) ) . ''; - } + // If this is a user field, get the display value. + $user_field = PMPro_Field_Group::get_field( $field ); + if ( ! empty( $user_field ) ) { + $r = $user_field->displayValue( $r, false ); } - // If this is a user field with an associative array of options, get the label(s) for the value(s). - $r = pmpro_get_label_for_user_field_value( $field, $r ); - // Check for arrays to reformat them. if ( is_array( $r ) ) { $r = implode( ', ', $r );