diff --git a/README.md b/README.md index c2c8af6f..ab6209bd 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ GNU Version 2 or Any Later Version ## Releases -[Official Release Version: 2.1.16](https://github.com/ultimatemember/ultimatemember/releases/tag/2.1.16). +[Official Release Version: 2.1.17](https://github.com/ultimatemember/ultimatemember/releases/tag/2.1.17). ## Changelog diff --git a/assets/js/um-scripts.js b/assets/js/um-scripts.js index 9ff1d5cb..28e0a1ab 100644 --- a/assets/js/um-scripts.js +++ b/assets/js/um-scripts.js @@ -124,14 +124,13 @@ jQuery(document).ready(function() { function unselectEmptyOption( e ) { var $element = jQuery( e.currentTarget ); var $selected = $element.find(':selected'); - if ( $selected.length > 1 ) { - $selected.each( function ( i, option ) { - if ( option.value === '' ) { - option.selected = false; - $element.trigger( 'change' ); - } - }); - } + + $selected.each( function ( i, option ) { + if ( option.value === '' ) { + option.selected = false; + $element.trigger( 'change' ); + } + }); } if ( typeof( jQuery.fn.select2 ) === 'function' ) { diff --git a/assets/js/um-scripts.min.js b/assets/js/um-scripts.min.js index e7bb2c81..8dc3db92 100644 --- a/assets/js/um-scripts.min.js +++ b/assets/js/um-scripts.min.js @@ -1 +1 @@ -function um_init_datetimepicker(){jQuery(".um-datepicker:not(.picker__input)").each(function(){var e=jQuery(this),t=!1;void 0!==e.attr("data-disabled_weekdays")&&""!==e.attr("data-disabled_weekdays")&&(t=JSON.parse(e.attr("data-disabled_weekdays")));var a=null;void 0!==e.attr("data-years")&&(a=e.attr("data-years"));var i=e.attr("data-date_min"),r=e.attr("data-date_max"),n=[],u=[];void 0!==i&&(n=i.split(",")),void 0!==r&&(u=r.split(","));var o=n.length?new Date(n):null,d=n.length?new Date(u):null;if(o&&"Invalid Date"==o.toString()&&3==n.length){var s=n[1]+"/"+n[2]+"/"+n[0];o=new Date(Date.parse(s))}if(d&&"Invalid Date"==d.toString()&&3==u.length){var l=u[1]+"/"+u[2]+"/"+u[0];d=new Date(Date.parse(l))}var c={disable:t,format:e.attr("data-format"),formatSubmit:"yyyy/mm/dd",hiddenName:!0,onOpen:function(){e.blur()},onClose:function(){e.blur()}};null!==a&&(c.selectYears=a),null!==o&&(c.min=o),null!==d&&(c.max=d),e.pickadate(c)}),jQuery(".um-timepicker:not(.picker__input)").each(function(){var e=jQuery(this);e.pickatime({format:e.attr("data-format"),interval:parseInt(e.attr("data-intervals")),formatSubmit:"HH:i",hiddenName:!0,onOpen:function(){e.blur()},onClose:function(){e.blur()}})})}function init_tipsy(){"function"==typeof jQuery.fn.tipsy&&(jQuery(".um-tip-n").tipsy({gravity:"n",opacity:1,live:"a.live",offset:3}),jQuery(".um-tip-w").tipsy({gravity:"w",opacity:1,live:"a.live",offset:3}),jQuery(".um-tip-e").tipsy({gravity:"e",opacity:1,live:"a.live",offset:3}),jQuery(".um-tip-s").tipsy({gravity:"s",opacity:1,live:"a.live",offset:3}))}jQuery(document).ready(function(){function a(e){var a=jQuery(e.currentTarget),t=a.find(":selected");1js_url . 'um-admin-field.js', array('jquery', 'wp-util'), ultimatemember_version, true ); + wp_register_script( 'um_admin_field', $this->js_url . 'um-admin-field.js', array('jquery', 'wp-util', 'wp-i18n'), ultimatemember_version, true ); wp_enqueue_script( 'um_admin_field' ); } diff --git a/includes/admin/core/class-admin-metabox.php b/includes/admin/core/class-admin-metabox.php index 7f4c7fc0..ceaf819f 100644 --- a/includes/admin/core/class-admin-metabox.php +++ b/includes/admin/core/class-admin-metabox.php @@ -2280,7 +2280,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Metabox' ) ) { $privacy_options = array( '1' => __( 'Everyone', 'ultimate-member' ), '2' => __( 'Members', 'ultimate-member' ), - '-1' => __( 'Only visible to profile owner and admins', 'ultimate-member' ), + '-1' => __( 'Only visible to profile owner and users who can edit other member accounts', 'ultimate-member' ), '-3' => __( 'Only visible to profile owner and specific roles', 'ultimate-member' ), '-2' => __( 'Only specific member roles', 'ultimate-member' ), ); diff --git a/includes/admin/core/class-admin-settings.php b/includes/admin/core/class-admin-settings.php index e1d8865e..b2c97670 100644 --- a/includes/admin/core/class-admin-settings.php +++ b/includes/admin/core/class-admin-settings.php @@ -99,15 +99,11 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) { $metakeys = array(); foreach ( UM()->builtin()->all_user_fields as $all_user_field ) { - if ( $all_user_field['type'] == 'user_location' ) { - $metakeys[] = $all_user_field['metakey'] . '_lat'; - $metakeys[] = $all_user_field['metakey'] . '_lng'; - $metakeys[] = $all_user_field['metakey'] . '_url'; - } else { - $metakeys[] = $all_user_field['metakey']; - } + $metakeys[] = $all_user_field['metakey']; } + $metakeys = apply_filters( 'um_metadata_same_page_update_ajax', $metakeys, UM()->builtin()->all_user_fields ); + if ( is_multisite() ) { $sites = get_sites( array( 'fields' => 'ids' ) ); @@ -336,7 +332,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) { 'tooltip' => __( 'Select the the user roles allowed to view this tab.', 'ultimate-member' ), 'options' => UM()->roles()->get_roles(), 'placeholder' => __( 'Choose user roles...', 'ultimate-member' ), - 'conditional' => array( 'profile_tab_' . $id . '_privacy', '=', 4 ), + 'conditional' => array( 'profile_tab_' . $id . '_privacy', '=', [ '4', '5' ] ), 'size' => 'small' ) ); @@ -651,7 +647,14 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) { '0' => __( 'No', 'ultimate-member' ), '1' => __( 'Yes', 'ultimate-member' ), ] - ) + ), + array( + 'id' => 'activation_link_expiry_time', + 'type' => 'number', + 'label' => __( 'Activation link lifetime', 'ultimate-member' ), + 'tooltip' => __( 'How long does an activation link live? Leave empty for endless links.', 'ultimate-member' ), + 'size' => 'small', + ), ) ), 'account' => array( diff --git a/includes/admin/core/packages/2.1.17-alpha/functions.php b/includes/admin/core/packages/2.1.17-alpha/functions.php new file mode 100644 index 00000000..01794741 --- /dev/null +++ b/includes/admin/core/packages/2.1.17-alpha/functions.php @@ -0,0 +1,140 @@ +ID, 'um_tab_slug', true ); + if ( UM()->external_integrations()->is_wpml_active() ) { + global $sitepress; + + $tab_id = $sitepress->get_object_id( $tab->ID, 'um_profile_tabs', true, $sitepress->get_default_language() ); + if ( $tab_id && $tab_id != $tab->ID ) { + $slug = get_post_meta( $tab_id, 'um_tab_slug', true ); + } + } + + return $slug; +} + + +function um_upgrade_profile_tabs2117() { + UM()->admin()->check_ajax_nonce(); + + um_maybe_unset_time_limit(); + + $labels = [ + 'name' => _x( 'Profile Tabs', 'Post Type General Name', 'ultimate-member' ), + 'singular_name' => _x( 'Profile tab', 'Post Type Singular Name', 'ultimate-member' ), + 'menu_name' => __( 'Profile Tabs', 'ultimate-member' ), + 'name_admin_bar' => __( 'Profile Tabs', 'ultimate-member' ), + 'archives' => __( 'Item Archives', 'ultimate-member' ), + 'attributes' => __( 'Item Attributes', 'ultimate-member' ), + 'parent_item_colon' => __( 'Parent Item:', 'ultimate-member' ), + 'all_items' => __( 'All Items', 'ultimate-member' ), + 'add_new_item' => __( 'Add New Item', 'ultimate-member' ), + 'add_new' => __( 'Add New', 'ultimate-member' ), + 'new_item' => __( 'New Item', 'ultimate-member' ), + 'edit_item' => __( 'Edit Item', 'ultimate-member' ), + 'update_item' => __( 'Update Item', 'ultimate-member' ), + 'view_item' => __( 'View Item', 'ultimate-member' ), + 'view_items' => __( 'View Items', 'ultimate-member' ), + 'search_items' => __( 'Search Item', 'ultimate-member' ), + 'not_found' => __( 'Not found', 'ultimate-member' ), + ]; + + $args = [ + 'label' => __( 'Profile Tabs', 'ultimate-member' ), + 'description' => __( '', 'ultimate-member' ), + 'labels' => $labels, + 'supports' => ['title', 'editor' ], + 'hierarchical' => false, + 'public' => false, + 'show_ui' => true, + 'show_in_menu' => false, + 'menu_position' => 5, + 'show_in_admin_bar' => false, + 'show_in_nav_menus' => false, + 'can_export' => true, + 'has_archive' => false, + 'exclude_from_search' => true, + 'publicly_queryable' => true, + 'capability_type' => 'page', + ]; + + register_post_type( 'um_profile_tabs', $args ); + + $profile_tabs = get_posts( [ + 'post_type' => 'um_profile_tabs', + 'orderby' => 'menu_order', + 'posts_per_page' => -1, + ] ); + + if ( ! empty( $profile_tabs ) ) { + $tabs_slugs = []; + + foreach ( $profile_tabs as $tab ) { + $slug = um_upgrade_get_slug2117( $tab ); + if ( ! empty( $slug ) && in_array( $slug, $tabs_slugs ) ) { + continue; + } + + if ( preg_match( "/[a-z0-9]+$/i", urldecode( $tab->post_name ) ) ) { + $tab_slug = sanitize_title( $tab->post_name ); + } else { + // otherwise use autoincrement and slug generator + $auto_increment = UM()->options()->get( 'custom_profiletab_increment' ); + $auto_increment = ! empty( $auto_increment ) ? $auto_increment : 1; + $tab_slug = "custom_profiletab_{$auto_increment}"; + } + + if ( UM()->external_integrations()->is_wpml_active() ) { + global $sitepress; + + $tab_id = $sitepress->get_object_id( $tab->ID, 'um_profile_tabs', true, $sitepress->get_default_language() ); + if ( $tab_id && $tab_id == $tab->ID ) { + update_post_meta( $tab->ID, 'um_tab_slug', $tab_slug ); + + $tabs_slugs[] = $tab_slug; + + if ( isset( $auto_increment ) ) { + $auto_increment++; + UM()->options()->update( 'custom_profiletab_increment', $auto_increment ); + } + + // show new profile tab by default - update UM Appearances > Profile Tabs settings + if ( UM()->options()->get( 'profile_tab_' . $tab_slug ) === '' ) { + UM()->options()->update( 'profile_tab_' . $tab_slug, '1' ); + UM()->options()->update( 'profile_tab_' . $tab_slug . '_privacy', '0' ); + } + } + } else { + update_post_meta( $tab->ID, 'um_tab_slug', $tab_slug ); + + $tabs_slugs[] = $tab_slug; + + if ( isset( $auto_increment ) ) { + $auto_increment++; + UM()->options()->update( 'custom_profiletab_increment', $auto_increment ); + } + + // show new profile tab by default - update UM Appearances > Profile Tabs settings + if ( UM()->options()->get( 'profile_tab_' . $tab_slug ) === '' ) { + UM()->options()->update( 'profile_tab_' . $tab_slug, '1' ); + UM()->options()->update( 'profile_tab_' . $tab_slug . '_privacy', '0' ); + } + } + } + } + + update_option( 'um_last_version_upgrade', '2.1.17-alpha' ); + + if ( ! empty( $profile_tabs ) ) { + wp_send_json_success( array( 'message' => __( 'Profile tabs have been updated successfully', 'ultimate-member' ) ) ); + } else { + wp_send_json_success( array( 'message' => __( 'Database has been updated successfully', 'ultimate-member' ) ) ); + } +} \ No newline at end of file diff --git a/includes/admin/core/packages/2.1.17-alpha/hooks.php b/includes/admin/core/packages/2.1.17-alpha/hooks.php new file mode 100644 index 00000000..a7bcb57a --- /dev/null +++ b/includes/admin/core/packages/2.1.17-alpha/hooks.php @@ -0,0 +1,5 @@ + 'profile_tabs2117', +); \ No newline at end of file diff --git a/includes/admin/core/packages/2.1.17-alpha/init.php b/includes/admin/core/packages/2.1.17-alpha/init.php new file mode 100644 index 00000000..8c69de57 --- /dev/null +++ b/includes/admin/core/packages/2.1.17-alpha/init.php @@ -0,0 +1,30 @@ + + + + \ No newline at end of file diff --git a/includes/class-config.php b/includes/class-config.php index 1f25edcc..c148fff1 100644 --- a/includes/class-config.php +++ b/includes/class-config.php @@ -577,6 +577,7 @@ if ( ! class_exists( 'um\Config' ) ) { 'member_directory_own_table' => 0, 'profile_show_html_bio' => 0, 'profile_noindex' => 0, + 'activation_link_expiry_time' => '', ); add_filter( 'um_get_tabs_from_config', '__return_true' ); diff --git a/includes/core/class-access.php b/includes/core/class-access.php index eccf0e6c..d1ad50b4 100644 --- a/includes/core/class-access.php +++ b/includes/core/class-access.php @@ -59,6 +59,10 @@ if ( ! class_exists( 'um\core\Access' ) ) { add_filter( 'get_pages', array( &$this, 'filter_protected_posts' ), 99, 2 ); //filter menu items add_filter( 'wp_nav_menu_objects', array( &$this, 'filter_menu' ), 99, 2 ); + + // turn on/off content replacement on the filter 'the_content' + add_action( 'get_header', array( &$this, 'replace_post_content_on' ), 12 ); + add_action( 'get_footer', array( &$this, 'replace_post_content_off' ), 8 ); //filter attachment add_filter( 'wp_get_attachment_url', array( &$this, 'filter_attachment' ), 99, 2 ); @@ -756,7 +760,6 @@ if ( ! class_exists( 'um\core\Access' ) ) { } $this->current_single_post = $post; - add_filter( 'the_content', array( &$this, 'replace_post_content' ), 9999, 1 ); /** * UM hook @@ -858,7 +861,6 @@ if ( ! class_exists( 'um\core\Access' ) ) { $post->post_content = stripslashes( $restricted_global_message ); $this->current_single_post = $post; - add_filter( 'the_content', array( &$this, 'replace_post_content' ), 9999, 1 ); if ( 'attachment' == $post->post_type ) { remove_filter( 'the_content', 'prepend_attachment' ); @@ -867,7 +869,6 @@ if ( ! class_exists( 'um\core\Access' ) ) { $post->post_content = ! empty( $restriction['_um_restrict_custom_message'] ) ? stripslashes( $restriction['_um_restrict_custom_message'] ) : ''; $this->current_single_post = $post; - add_filter( 'the_content', array( &$this, 'replace_post_content' ), 9999, 1 ); if ( 'attachment' == $post->post_type ) { remove_filter( 'the_content', 'prepend_attachment' ); @@ -947,7 +948,6 @@ if ( ! class_exists( 'um\core\Access' ) ) { $post->post_content = stripslashes( $restricted_global_message ); $this->current_single_post = $post; - add_filter( 'the_content', array( &$this, 'replace_post_content' ), 9999, 1 ); if ( 'attachment' == $post->post_type ) { remove_filter( 'the_content', 'prepend_attachment' ); @@ -956,7 +956,6 @@ if ( ! class_exists( 'um\core\Access' ) ) { $post->post_content = ! empty( $restriction['_um_restrict_custom_message'] ) ? stripslashes( $restriction['_um_restrict_custom_message'] ) : ''; $this->current_single_post = $post; - add_filter( 'the_content', array( &$this, 'replace_post_content' ), 9999, 1 ); if ( 'attachment' == $post->post_type ) { remove_filter( 'the_content', 'prepend_attachment' ); @@ -1033,16 +1032,41 @@ if ( ! class_exists( 'um\core\Access' ) ) { /** - * @param $content + * Replace the content on the filter 'the_content' * + * @param $content * @return string */ function replace_post_content( $content ) { - $content = $this->current_single_post->post_content; + if ( ! empty( $this->current_single_post ) ) { + $content = $this->current_single_post->post_content; + } return $content; } + /** + * Turn on the content replacement on the filter 'the_content' + * + * @hooked get_header 12 + * @since 2.1.17 + */ + public function replace_post_content_on() { + add_filter( 'the_content', array( $this, 'replace_post_content' ), 9999, 1 ); + } + + + /** + * Turn off the content replacement on the filter 'the_content' + * + * @hooked get_footer 8 + * @since 2.1.17 + */ + public function replace_post_content_off() { + remove_filter( 'the_content', array( $this, 'replace_post_content' ), 9999 ); + } + + /** * Disable comments if user has not permission to access this post * diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index befe7ea3..c4a05f35 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -256,6 +256,14 @@ if ( ! class_exists( 'um\core\Fields' ) ) { do_action( 'um_delete_custom_field', $id, $args ); update_option( 'um_fields', $fields ); + + global $wpdb; + $forms = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'um_form'" ); + foreach ( $forms as $form_id ) { + $form_fields = get_post_meta( $form_id, '_um_custom_fields', true ); + unset( $form_fields[ $id ] ); + update_post_meta( $form_id, '_um_custom_fields', $form_fields ); + } } } @@ -1045,9 +1053,6 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( ! $this->editing || 'custom' == $this->set_mode ) { // show default on register screen if there is default if ( isset( $data['default'] ) ) { - if ( ! is_array( $data['default'] ) && strstr( $data['default'], ', ' ) ) { - $data['default'] = explode( ', ', $data['default'] ); - } if ( ! is_array( $data['default'] ) && $data['default'] === $value ) { return true; @@ -1061,6 +1066,14 @@ if ( ! class_exists( 'um\core\Fields' ) ) { return true; } + // default value with comma + if ( is_string( $data['default'] ) && strstr( $data['default'], ',' ) ) { + $choices = array_map( 'trim', explode( ',', $data['default'] ) ); + if ( in_array( $value, $choices ) ) { + return true; + } + } + } } else { @@ -2395,11 +2408,17 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $name = $key; } - $placeholder = sprintf( __( 'Confirm %s', 'ultimate-member' ), $data['label'] ); + if( ! empty( $placeholder ) && ! isset( $data['label'] ) ){ + $placeholder = sprintf( __( 'Confirm %s', 'ultimate-member' ), $placeholder ); + }else if( isset( $data['label'] ) ){ + $placeholder = sprintf( __( 'Confirm %s', 'ultimate-member' ), $data['label'] ); + } + - $output .= ' + $output .= ''; + - '; + $output .= ''; if ( $this->is_error( $key ) ) { $output .= $this->field_error( $this->show_error( $key ) ); diff --git a/includes/core/class-files.php b/includes/core/class-files.php index ee5eb8c4..6f2a1a33 100644 --- a/includes/core/class-files.php +++ b/includes/core/class-files.php @@ -39,7 +39,7 @@ if ( ! class_exists( 'um\core\Files' ) ) { $this->setup_paths(); - add_action( 'template_redirect', array( &$this, 'download_routing' ) ); + add_action( 'template_redirect', array( &$this, 'download_routing' ), 1 ); $this->fonticon = array( 'pdf' => array('icon' => 'um-faicon-file-pdf-o', 'color' => '#D24D4D' ), diff --git a/includes/core/class-form.php b/includes/core/class-form.php index 6bb7629e..9fa80c5d 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -366,12 +366,15 @@ if ( ! class_exists( 'um\core\Form' ) ) { */ $this->post_form = apply_filters( 'um_submit_post_form', $_POST ); + if ( isset( $this->post_form[ UM()->honeypot ] ) && $this->post_form[ UM()->honeypot ] != '' ) { + wp_die( __( 'Hello, spam bot!', 'ultimate-member' ) ); + } + $this->post_form = $this->beautify( $this->post_form ); + $this->post_form['submitted'] = $this->post_form; $this->form_data = UM()->query()->post_data( $this->form_id ); - $this->post_form['submitted'] = $this->post_form; - $this->post_form = array_merge( $this->form_data, $this->post_form ); // Remove role from post_form at first if role ! empty and there aren't custom fields with role name @@ -438,10 +441,6 @@ if ( ! class_exists( 'um\core\Form' ) ) { } - if ( isset( $_POST[ UM()->honeypot ] ) && $_POST[ UM()->honeypot ] != '' ) { - wp_die( __( 'Hello, spam bot!', 'ultimate-member' ) ); - } - /** * UM hook * @@ -518,18 +517,19 @@ if ( ! class_exists( 'um\core\Form' ) ) { /** * Beautify form data + * * @param array $form * @return array $form */ - function beautify( $form ){ + function beautify( $form ) { - if (isset($form['form_id'])){ + if ( isset( $form['form_id'] ) ) { $this->form_suffix = '-' . $form['form_id']; $this->processing = $form['form_id']; - foreach( $form as $key => $value ){ + foreach ( $form as $key => $value ) { if ( strstr( $key, $this->form_suffix ) ) { $a_key = str_replace( $this->form_suffix, '', $key ); $form[ $a_key ] = $value; diff --git a/includes/core/class-mail.php b/includes/core/class-mail.php index 121fd0df..bef34181 100644 --- a/includes/core/class-mail.php +++ b/includes/core/class-mail.php @@ -405,6 +405,8 @@ if ( ! class_exists( 'um\core\Mail' ) ) { return; } + do_action( 'um_before_email_notification_sending', $email, $template, $args ); + $this->attachments = array(); $this->headers = 'From: '. stripslashes( UM()->options()->get('mail_from') ) .' <'. UM()->options()->get('mail_from_addr') .'>' . "\r\n"; @@ -450,6 +452,8 @@ if ( ! class_exists( 'um\core\Mail' ) ) { // Send mail wp_mail( $email, $this->subject, $this->message, $this->headers, $this->attachments ); + + do_action( 'um_after_email_notification_sending', $email, $template, $args ); } diff --git a/includes/core/class-member-directory-meta.php b/includes/core/class-member-directory-meta.php index 87211a39..44f6e99b 100644 --- a/includes/core/class-member-directory-meta.php +++ b/includes/core/class-member-directory-meta.php @@ -56,51 +56,25 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) { function on_delete_custom_field( $metakey, $args ) { $metakeys = get_option( 'um_usermeta_fields', array() ); - if ( $args['type'] == 'user_location' ) { - if ( array_intersect( array( $metakey . '_lat', $metakey . '_lng', $metakey . '_url' ), $metakeys ) ) { - if ( false !== $searched = array_search( $metakey . '_lat', $metakeys ) ) { - unset( $metakeys[ $searched ] ); - } - if ( false !== $searched = array_search( $metakey . '_lng', $metakeys ) ) { - unset( $metakeys[ $searched ] ); - } - if ( false !== $searched = array_search( $metakey . '_url', $metakeys ) ) { - unset( $metakeys[ $searched ] ); - } + if ( in_array( $metakey, $metakeys ) ) { + unset( $metakeys[ array_search( $metakey, $metakeys ) ] ); - global $wpdb; + global $wpdb; - $wpdb->query( $wpdb->prepare( - "DELETE FROM {$wpdb->prefix}um_metadata - WHERE um_key = %s OR - um_key = %s OR - um_key = %s", - $metakey . '_lat', - $metakey . '_lng', - $metakey . '_url' - ) ); + $wpdb->delete( + "{$wpdb->prefix}um_metadata", + array( + 'um_key' => $metakey + ), + array( + '%s' + ) + ); - update_option( 'um_usermeta_fields', array_values( $metakeys ) ); - } - } else { - if ( in_array( $metakey, $metakeys ) ) { - unset( $metakeys[ array_search( $metakey, $metakeys ) ] ); - - global $wpdb; - - $wpdb->delete( - "{$wpdb->prefix}um_metadata", - array( - 'um_key' => $metakey - ), - array( - '%s' - ) - ); - - update_option( 'um_usermeta_fields', array_values( $metakeys ) ); - } + update_option( 'um_usermeta_fields', array_values( $metakeys ) ); } + + do_action( 'um_metadata_on_delete_custom_field', $metakeys, $metakey, $args ); } @@ -113,32 +87,12 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) { function on_new_field_added( $metakey, $args ) { $metakeys = get_option( 'um_usermeta_fields', array() ); - if ( $args['type'] == 'user_location' ) { - $update = false; - if ( ! in_array( $metakey . '_lat', $metakeys ) ) { - $update = true; - $metakeys[] = $metakey . '_lat'; - } - - if ( ! in_array( $metakey . '_lng', $metakeys ) ) { - $update = true; - $metakeys[] = $metakey . '_lng'; - } - - if ( ! in_array( $metakey . '_url', $metakeys ) ) { - $update = true; - $metakeys[] = $metakey . '_url'; - } - - if ( $update ) { - update_option( 'um_usermeta_fields', array_values( $metakeys ) ); - } - } else { - if ( ! in_array( $metakey, $metakeys ) ) { - $metakeys[] = $metakey; - update_option( 'um_usermeta_fields', array_values( $metakeys ) ); - } + if ( ! in_array( $metakey, $metakeys ) ) { + $metakeys[] = $metakey; + update_option( 'um_usermeta_fields', array_values( $metakeys ) ); } + + do_action( 'um_metadata_on_new_field_added', $metakeys, $metakey, $args ); } @@ -291,7 +245,10 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) { $value = trim( stripslashes( $value ) ); - $this->where_clauses[] = $wpdb->prepare( "{$join_slug}{$i}.um_key = %s AND {$join_slug}{$i}.um_value = %s", $field, $value ); + $compare = apply_filters( 'um_members_directory_filter_text', '=', $field ); + $value = apply_filters( 'um_members_directory_filter_text_meta_value', $value, $field ); + + $this->where_clauses[] = $wpdb->prepare( "{$join_slug}{$i}.um_key = %s AND {$join_slug}{$i}.um_value {$compare} %s", $field, $value ); if ( ! $is_default ) { $this->custom_filters_in_query[ $field ] = $value; diff --git a/includes/core/class-member-directory.php b/includes/core/class-member-directory.php index 6cee115e..3aceb388 100644 --- a/includes/core/class-member-directory.php +++ b/includes/core/class-member-directory.php @@ -687,7 +687,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) { 'OR', array( 'key' => $field, 'value' => trim( $value ), - 'compare' => 'LIKE', + 'compare' => apply_filters( 'um_members_directory_filter_text', 'LIKE', $field ) ), ); @@ -1871,7 +1870,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) { $field_query = array( 'key' => $field, 'value' => $value, - 'compare' => '=', + 'compare' => apply_filters( 'um_members_directory_filter_text', '=', $field ), ); break; @@ -2212,7 +2211,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) { $dropdown_actions = $this->build_user_actions_list( $user_id ); $actions = array(); - $can_edit = UM()->roles()->um_current_user_can( 'edit', $user_id ) || UM()->roles()->um_user_can( 'can_edit_everyone' ); + $can_edit = UM()->roles()->um_current_user_can( 'edit', $user_id ); // Replace hook 'um_members_just_after_name' ob_start(); diff --git a/includes/core/class-permalinks.php b/includes/core/class-permalinks.php index 79f69fc9..b22185c5 100644 --- a/includes/core/class-permalinks.php +++ b/includes/core/class-permalinks.php @@ -187,6 +187,11 @@ if ( ! class_exists( 'um\core\Permalinks' ) ) { wp_die( __( 'This activation link is expired or have already been used.', 'ultimate-member' ) ); } + $account_secret_hash_expiry = get_user_meta( $user_id, 'account_secret_hash_expiry', true ); + if ( ! empty( $account_secret_hash_expiry ) && time() > $account_secret_hash_expiry ) { + wp_die( __( 'This activation link is expired.', 'ultimate-member' ) ); + } + um_fetch_user( $user_id ); UM()->user()->approve(); um_reset_user(); diff --git a/includes/core/class-profile.php b/includes/core/class-profile.php index 1cffe05c..85830f79 100644 --- a/includes/core/class-profile.php +++ b/includes/core/class-profile.php @@ -101,7 +101,8 @@ if ( ! class_exists( 'um\core\Profile' ) ) { 1 => __( 'Guests only', 'ultimate-member' ), 2 => __( 'Members only', 'ultimate-member' ), 3 => __( 'Only the owner', 'ultimate-member' ), - 4 => __( 'Specific roles', 'ultimate-member' ), + 4 => __( 'Only specific roles', 'ultimate-member' ), + 5 => __( 'Owner and specific roles', 'ultimate-member' ), ); return $privacy; @@ -227,6 +228,25 @@ if ( ! class_exists( 'um\core\Profile' ) ) { } } break; + case 5: + if ( is_user_logged_in() ) { + // check profile owner if not - check privacy roles settings + $can_view = get_current_user_id() === $target_id; + + if ( ! $can_view ) { + if ( isset( $tab_data['default_privacy'] ) ) { + $roles = isset( $tab_data['default_privacy_roles'] ) ? $tab_data['default_privacy_roles'] : array(); + } else { + $roles = (array) UM()->options()->get( 'profile_tab_' . $tab . '_roles' ); + } + + $current_user_roles = um_user( 'roles' ); + if ( ! empty( $current_user_roles ) && count( array_intersect( $current_user_roles, $roles ) ) > 0 ) { + $can_view = true; + } + } + } + break; default: $can_view = true; @@ -322,10 +342,9 @@ if ( ! class_exists( 'um\core\Profile' ) ) { } else { if ( ! empty( $tabs ) ) { foreach ( $tabs as $k => $tab ) { - if ( ! empty( $tab['hidden'] ) ) { - $this->active_tab = $k; - break; - } + // set first tab in order + $this->active_tab = $k; + break; } } } diff --git a/includes/core/class-roles-capabilities.php b/includes/core/class-roles-capabilities.php index 233e7cec..c4e10358 100644 --- a/includes/core/class-roles-capabilities.php +++ b/includes/core/class-roles-capabilities.php @@ -643,13 +643,18 @@ if ( ! class_exists( 'um\core\Roles_Capabilities' ) ) { $return = 0; } } else { - if ( ! um_user( 'can_edit_everyone' ) ) { + + if ( ! um_user( 'can_access_private_profile' ) && UM()->user()->is_private_profile( $user_id ) ) { $return = 0; } else { - if ( um_user( 'can_edit_roles' ) && ( empty( $current_user_roles ) || count( array_intersect( $current_user_roles, um_user( 'can_edit_roles' ) ) ) <= 0 ) ) { + if ( ! um_user( 'can_edit_everyone' ) ) { $return = 0; } else { - $return = 1; + if ( um_user( 'can_edit_roles' ) && ( empty( $current_user_roles ) || count( array_intersect( $current_user_roles, um_user( 'can_edit_roles' ) ) ) <= 0 ) ) { + $return = 0; + } else { + $return = 1; + } } } } diff --git a/includes/core/class-user.php b/includes/core/class-user.php index bf29c86b..2272317a 100644 --- a/includes/core/class-user.php +++ b/includes/core/class-user.php @@ -1336,6 +1336,13 @@ if ( ! class_exists( 'um\core\User' ) ) { $this->profile['account_secret_hash'] = UM()->validation()->generate(); $this->update_usermeta_info( 'account_secret_hash' ); + + $expiry_time = UM()->options()->get( 'activation_link_expiry_time' ); + if ( ! empty( $expiry_time ) && is_numeric( $expiry_time ) ) { + $this->profile['account_secret_hash_expiry'] = time() + $expiry_time; + $this->update_usermeta_info( 'account_secret_hash_expiry' ); + } + /** * UM hook * @@ -1436,6 +1443,7 @@ if ( ! class_exists( 'um\core\User' ) ) { $this->set_status( 'approved' ); $this->delete_meta( 'account_secret_hash' ); + $this->delete_meta( 'account_secret_hash_expiry' ); /** * UM hook diff --git a/includes/core/um-actions-account.php b/includes/core/um-actions-account.php index 7a5f4c1f..984d7cbd 100644 --- a/includes/core/um-actions-account.php +++ b/includes/core/um-actions-account.php @@ -453,7 +453,7 @@ function um_before_account_notifications( $args = array() ) { $output = UM()->account()->get_tab_fields( 'notifications', $args ); if ( substr_count( $output, '_enable_new_' ) ) { ?> -

+

+ */ + do_action( 'um_submit_form_errors_hook__blockedips', $args ); + + + /** + * UM hook + * + * @type action + * @title um_submit_form_errors_hook__blockedemails + * @description Submit form validation + * @input_vars + * [{"var":"$args","type":"array","desc":"Form Arguments"}] + * @change_log + * ["Since: 2.0"] + * @usage add_action( 'um_submit_form_errors_hook__blockedemails', 'function_name', 10, 1 ); + * @example + * + */ + do_action( 'um_submit_form_errors_hook__blockedemails', $args ); if ( $mode == 'register' ) { + /** * UM hook * @@ -137,6 +177,7 @@ function um_submit_form_errors_hook( $args ) { } elseif ( $mode == 'profile' ) { + /** * UM hook * @@ -158,50 +199,9 @@ function um_submit_form_errors_hook( $args ) { */ do_action( 'um_submit_form_errors_hook__profile', $args ); - } + } elseif ( $mode == 'login' ) { - /** - * UM hook - * - * @type action - * @title um_submit_form_errors_hook__blockedips - * @description Submit form validation - * @input_vars - * [{"var":"$args","type":"array","desc":"Form Arguments"}] - * @change_log - * ["Since: 2.0"] - * @usage add_action( 'um_submit_form_errors_hook__blockedips', 'function_name', 10, 1 ); - * @example - * - */ - do_action( 'um_submit_form_errors_hook__blockedips', $args ); - /** - * UM hook - * - * @type action - * @title um_submit_form_errors_hook__blockedemails - * @description Submit form validation - * @input_vars - * [{"var":"$args","type":"array","desc":"Form Arguments"}] - * @change_log - * ["Since: 2.0"] - * @usage add_action( 'um_submit_form_errors_hook__blockedemails', 'function_name', 10, 1 ); - * @example - * - */ - do_action( 'um_submit_form_errors_hook__blockedemails', $args ); - if ( $mode == 'login' ) { /** * UM hook * @@ -222,6 +222,8 @@ function um_submit_form_errors_hook( $args ) { * ?> */ do_action( 'um_submit_form_errors_hook_login', $args ); + + /** * UM hook * @@ -243,27 +245,12 @@ function um_submit_form_errors_hook( $args ) { */ do_action( 'um_submit_form_errors_hook_logincheck', $args ); - } else { - /** - * UM hook - * - * @type action - * @title um_submit_form_errors_hook_ - * @description Submit form validation - * @input_vars - * [{"var":"$args","type":"array","desc":"Form Arguments"}] - * @change_log - * ["Since: 2.0"] - * @usage add_action( 'um_submit_form_errors_hook_', 'function_name', 10, 1 ); - * @example - * - */ - do_action( 'um_submit_form_errors_hook_', $args ); + } + + + if ( $mode != 'login' ) { + + /** * UM hook * @@ -285,6 +272,28 @@ function um_submit_form_errors_hook( $args ) { */ do_action( 'um_submit_form_errors_hook__blockedwords', $args ); + + /** + * UM hook + * + * @type action + * @title um_submit_form_errors_hook_ + * @description Submit form validation + * @input_vars + * [{"var":"$args","type":"array","desc":"Form Arguments"}] + * @change_log + * ["Since: 2.0"] + * @usage add_action( 'um_submit_form_errors_hook_', 'function_name', 10, 1 ); + * @example + * + */ + do_action( 'um_submit_form_errors_hook_', $args ); + } } @@ -411,13 +420,25 @@ function um_submit_form_errors_hook_( $args ) { $form_id = $args['form_id']; $mode = $args['mode']; $fields = unserialize( $args['custom_fields'] ); - $um_profile_photo = um_profile('profile_photo'); + $um_profile_photo = um_profile('profile_photo'); if ( get_post_meta( $form_id, '_um_profile_photo_required', true ) && ( empty( $args['profile_photo'] ) && empty( $um_profile_photo ) ) ) { UM()->form()->add_error('profile_photo', __( 'Profile Photo is required.', 'ultimate-member' ) ); } if ( ! empty( $fields ) ) { + + $can_edit = false; + $current_user_roles = []; + if ( is_user_logged_in() ) { + + $can_edit = UM()->roles()->um_current_user_can( 'edit', $args['user_id'] ); + + um_fetch_user( get_current_user_id() ); + $current_user_roles = um_user( 'roles' ); + um_reset_user(); + } + foreach ( $fields as $key => $array ) { if ( $mode == 'profile' ) { @@ -427,13 +448,52 @@ function um_submit_form_errors_hook_( $args ) { } } - if ( isset( $array['public'] ) && -2 == $array['public'] && ! empty( $array['roles'] ) && is_user_logged_in() ) { - $current_user_roles = um_user( 'roles' ); - if ( empty( $current_user_roles ) || count( array_intersect( $current_user_roles, $array['roles'] ) ) <= 0 ) { - continue; + $can_view = true; + if ( isset( $array['public'] ) && $mode != 'register' ) { + + switch ( $array['public'] ) { + case '1': // Everyone + break; + case '2': // Members + if ( ! is_user_logged_in() ) { + $can_view = false; + } + break; + case '-1': // Only visible to profile owner and admins + if ( ! is_user_logged_in() ) { + $can_view = false; + } elseif ( $args['user_id'] != get_current_user_id() && ! $can_edit ) { + $can_view = false; + } + break; + case '-2': // Only specific member roles + if ( ! is_user_logged_in() ) { + $can_view = false; + } elseif ( ! empty( $array['roles'] ) && count( array_intersect( $current_user_roles, $array['roles'] ) ) <= 0 ) { + $can_view = false; + } + break; + case '-3': // Only visible to profile owner and specific roles + if ( ! is_user_logged_in() ) { + $can_view = false; + } elseif ( $args['user_id'] != get_current_user_id() && ! empty( $array['roles'] ) && count( array_intersect( $current_user_roles, $array['roles'] ) ) <= 0 ) { + $can_view = false; + } + break; + default: + $can_view = apply_filters( 'um_can_view_field_custom', $can_view, $array ); + break; } + } + $can_view = apply_filters( 'um_can_view_field', $can_view, $array ); + + if ( ! $can_view ) { + continue; + } + + /** * UM hook * diff --git a/includes/core/um-actions-login.php b/includes/core/um-actions-login.php index 6de065fb..b9d12f35 100644 --- a/includes/core/um-actions-login.php +++ b/includes/core/um-actions-login.php @@ -75,7 +75,7 @@ function um_submit_form_errors_hook_login( $args ) { // if there is an error notify wp if ( UM()->form()->has_error( $field ) || UM()->form()->has_error( $user_password ) || UM()->form()->count_errors() > 0 ) { - do_action( 'wp_login_failed', $user_name ); + do_action( 'wp_login_failed', $user_name, UM()->form()->errors ); } } add_action( 'um_submit_form_errors_hook_login', 'um_submit_form_errors_hook_login', 10 ); diff --git a/includes/core/um-actions-profile.php b/includes/core/um-actions-profile.php index 19afb128..f68dcc27 100644 --- a/includes/core/um-actions-profile.php +++ b/includes/core/um-actions-profile.php @@ -1294,42 +1294,38 @@ function um_pre_profile_shortcode( $args ) { */ extract( $args ); - if ( $mode == 'profile' && UM()->fields()->editing == false ) { - UM()->fields()->viewing = 1; - - if ( um_get_requested_user() ) { - if ( ! um_can_view_profile( um_get_requested_user() ) && ! um_is_myprofile() ) { - um_redirect_home( um_get_requested_user(), um_is_myprofile() ); + if ( $mode == 'profile' ) { + if ( UM()->fields()->editing ) { + if ( um_get_requested_user() ) { + if ( ! UM()->roles()->um_current_user_can( 'edit', um_get_requested_user() ) ) { + um_redirect_home( um_get_requested_user(), um_is_myprofile() ); + } + um_fetch_user( um_get_requested_user() ); } - - if ( ! UM()->roles()->um_current_user_can( 'edit', um_get_requested_user() ) ) { - UM()->user()->cannot_edit = 1; - } - - um_fetch_user( um_get_requested_user() ); } else { - if ( ! is_user_logged_in() ) { - um_redirect_home( um_get_requested_user(), um_is_myprofile() ); - } + UM()->fields()->viewing = 1; - if ( ! um_user( 'can_edit_profile' ) ) { - UM()->user()->cannot_edit = 1; + if ( um_get_requested_user() ) { + if ( ! um_can_view_profile( um_get_requested_user() ) && ! um_is_myprofile() ) { + um_redirect_home( um_get_requested_user(), um_is_myprofile() ); + } + + if ( ! UM()->roles()->um_current_user_can( 'edit', um_get_requested_user() ) ) { + UM()->user()->cannot_edit = 1; + } + + um_fetch_user( um_get_requested_user() ); + } else { + if ( ! is_user_logged_in() ) { + um_redirect_home( um_get_requested_user(), um_is_myprofile() ); + } + + if ( ! um_user( 'can_edit_profile' ) ) { + UM()->user()->cannot_edit = 1; + } } } } - - if ( $mode == 'profile' && UM()->fields()->editing == true ) { - UM()->fields()->editing = 1; - - if ( um_get_requested_user() ) { - if ( ! UM()->roles()->um_current_user_can( 'edit', um_get_requested_user() ) ) { - um_redirect_home( um_get_requested_user(), um_is_myprofile() ); - } - um_fetch_user( um_get_requested_user() ); - } - - } - } add_action( 'um_pre_profile_shortcode', 'um_pre_profile_shortcode' ); diff --git a/includes/um-short-functions.php b/includes/um-short-functions.php index 67c4c4f3..5960e2ce 100644 --- a/includes/um-short-functions.php +++ b/includes/um-short-functions.php @@ -1514,7 +1514,12 @@ function um_can_view_field( $data ) { if ( isset( $data['public'] ) && UM()->fields()->set_mode != 'register' ) { + $can_edit = false; + $current_user_roles = []; if ( is_user_logged_in() ) { + + $can_edit = UM()->roles()->um_current_user_can( 'edit', um_user( 'ID' ) ); + $previous_user = um_user( 'ID' ); um_fetch_user( get_current_user_id() ); @@ -1523,47 +1528,32 @@ function um_can_view_field( $data ) { } switch ( $data['public'] ) { - case '1': - $can_view = true; + case '1': // Everyone break; - case '2': + case '2': // Members if ( ! is_user_logged_in() ) { $can_view = false; } break; - case '-1': + case '-1': // Only visible to profile owner and users who can edit other member accounts if ( ! is_user_logged_in() ) { $can_view = false; - } else { - if ( ! um_is_user_himself() && ! UM()->roles()->um_user_can( 'can_edit_everyone' ) ) { - $can_view = false; - } + } elseif ( ! um_is_user_himself() && ! $can_edit ) { + $can_view = false; } break; - case '-2': + case '-2': // Only specific member roles if ( ! is_user_logged_in() ) { $can_view = false; - } else { - if ( ! empty( $data['roles'] ) ) { - if ( empty( $current_user_roles ) || count( array_intersect( $current_user_roles, $data['roles'] ) ) <= 0 ) { - $can_view = false; - } - } + } elseif ( ! empty( $data['roles'] ) && count( array_intersect( $current_user_roles, $data['roles'] ) ) <= 0 ) { + $can_view = false; } break; - case '-3': + case '-3': // Only visible to profile owner and specific roles if ( ! is_user_logged_in() ) { $can_view = false; - } else { - if ( ! um_is_core_page( 'profile' ) ) { - if ( empty( $current_user_roles ) || ( ! empty( $data['roles'] ) && count( array_intersect( $current_user_roles, $data['roles'] ) ) <= 0 ) ) { - $can_view = false; - } - } else { - if ( ! um_is_user_himself() && ( empty( $current_user_roles ) || ( ! empty( $data['roles'] ) && count( array_intersect( $current_user_roles, $data['roles'] ) ) <= 0 ) ) ) { - $can_view = false; - } - } + } elseif ( ! um_is_user_himself() && ! empty( $data['roles'] ) && count( array_intersect( $current_user_roles, $data['roles'] ) ) <= 0 ) { + $can_view = false; } break; default: @@ -1585,10 +1575,6 @@ function um_can_view_field( $data ) { * @return bool */ function um_can_view_profile( $user_id ) { - if ( UM()->roles()->um_current_user_can( 'edit', $user_id ) ) { - return true; - } - if ( ! is_user_logged_in() ) { return ! UM()->user()->is_private_profile( $user_id ); } @@ -1618,6 +1604,7 @@ function um_can_view_profile( $user_id ) { return false; } } + um_fetch_user( $temp_id ); return true; } diff --git a/readme.txt b/readme.txt index c80c580b..6e131ea8 100644 --- a/readme.txt +++ b/readme.txt @@ -7,7 +7,7 @@ Tags: community, member, membership, user-profile, user-registration Requires PHP: 5.6 Requires at least: 5.0 Tested up to: 5.7 -Stable tag: 2.1.16 +Stable tag: 2.1.17 License: GNU Version 2 or Any Later Version License URI: http://www.gnu.org/licenses/gpl-3.0.txt @@ -155,6 +155,30 @@ The plugin works with popular caching plugins by automatically excluding Ultimat * To learn more about version 2.1 please see this [docs](https://docs.ultimatemember.com/article/1512-upgrade-2-1-0) * UM2.1+ is a significant update to the Member Directories' code base from 2.0.x. Please make sure you take a full-site backup with restore point before updating the plugin += 2.1.17: April 14, 2021 = + +* Enhancements: + - Added: Activation link expiration setting (#803) + - Added: 'Owner and specific roles' privacy type for the Profile tabs (#773) + - Added: Hooks `um_before_email_notification_sending` and `um_after_email_notification_sending` regarding #743 pull-request + - Added: Hooks for integration member directory filters with different conditions for query to DB + +* Bugfixes: + - Fixed: Editing private profiles capability. Removed the priority for "Can edit other member accounts?" capability when the account is private. (#805) + - Fixed: PHP notice when the admin filtering field has the not array default value (e.g. bool) + - Fixed: The conflict with [Disable Blog](https://wordpress.org/plugins/disable-blog/) plugin regarding this [issue](https://wordpress.org/support/topic/ultimate-member-file-image-download/). `download_routing` callback has the highest priority now. + - Fixed: Fields privacy functionality. This function `um_can_view_field()` has been rewritten (#780) + - Fixed: Fields privacy + form validation functionality. Skip the field's validation that aren't visible on the form based on the privacy settings (#795) + - Fixed: PHP notice in the confirm password label [wp.org thread](https://wordpress.org/support/topic/undefined-index-label-php-notice/) + - Fixed: Confirm password placeholder when label is empty + - Fixed: Permanently deleting form's field (#786) + - Fixed: Default value with comma for checkbox field-type (#729) + - Fixed: Gutenberg blocks conditional settings (#793) + - Fixed: Getting active Profile tab when some of them are hidden + - Fixed: Restricted content message is showing twice and removing header, avoid the conflicts with themes that use 'the_content' filter in header or footer (#799) + - Fixed: "Is required" validation for the multiselect field-type when it's empty on submit + - Fixed: Using 'wp_login_failed' hook, added 2nd parameter required since WordPress 5.4 version (#810) + = 2.1.16: March 10, 2021 = * Enhancements: diff --git a/ultimate-member.php b/ultimate-member.php index 4fe50ddb..3a43949e 100644 --- a/ultimate-member.php +++ b/ultimate-member.php @@ -3,7 +3,7 @@ Plugin Name: Ultimate Member Plugin URI: http://ultimatemember.com/ Description: The easiest way to create powerful online communities and beautiful user profiles with WordPress -Version: 2.1.16 +Version: 2.1.17 Author: Ultimate Member Author URI: http://ultimatemember.com/ Text Domain: ultimate-member