- fixed Install Info settings section on PHP7.1;

- GDPR compatibility;
- added support for GDPR Personal Data Exporter;
- added support for GDPR Personal Data Eraser;
- added feature: New privacy field to form builder for GDPR consent collection;
- added GDPR privacy policy guide text;
This commit is contained in:
nikitozzzzzzz
2018-05-22 11:17:18 +03:00
parent ef82f19d16
commit 8414586f45
11 changed files with 633 additions and 4 deletions
+22
View File
@@ -0,0 +1,22 @@
(function( $ ) {
'use strict';
$(document).on('click', "a.um-toggle-gdpr" ,function() {
var me = jQuery(this);
$( ".um-gdpr-content" ).toggle( "fast", function() {
if( $( ".um-gdpr-content" ).is(':visible') ){
me.text( me.data('toggle-hide') );
}
if( $( ".um-gdpr-content" ).is(':hidden') ){
me.text( me.data('toggle-show') );
}
});
});
})( jQuery );
+316
View File
@@ -0,0 +1,316 @@
<?php
namespace um\admin\core;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) exit;
if ( ! class_exists( 'um\admin\core\Admin_GDPR' ) ) {
/**
* Class Admin_GDPR
* @package um\admin\core
*/
class Admin_GDPR {
/**
* @var array
*/
var $meta_associations = array();
/**
* Admin_GDPR constructor.
*/
function __construct() {
add_action( 'init', array( &$this, 'init_fields' ), 10 );
add_action( 'admin_init', array( &$this, 'plugin_add_suggested_privacy_content' ), 20 );
add_filter( 'wp_privacy_personal_data_exporters', array( &$this, 'plugin_register_exporters' ) );
add_filter( 'wp_privacy_personal_data_erasers', array( &$this, 'plugin_register_erasers' ) );
add_action( 'um_admin_custom_register_metaboxes', array( &$this, 'add_metabox_register' ) );
}
/**
*
*/
function add_metabox_register() {
add_meta_box(
"um-admin-form-register_gdpr",
__( 'Privacy Policy', 'ultimate-member' ),
array( UM()->metabox(), 'load_metabox_form' ),
'um_form',
'side',
'default'
);
}
/**
*
*/
function init_fields() {
$this->meta_associations = array(
'account_status' => __( 'Account Status', 'ultimate-member' ),
'submitted' => __( 'Submitted data on Registration', 'ultimate-member' ),
'form_id' => __( 'Registration Form ID', 'ultimate-member' ),
'timestamp' => __( 'Registration Timestamp', 'ultimate-member' ),
'request' => __( 'Registration Request', 'ultimate-member' ),
'_wpnonce' => __( 'Registration Nonce', 'ultimate-member' ),
'_wp_http_referer' => __( 'Registration HTTP referer', 'ultimate-member' ),
'role' => __( 'Community Role', 'ultimate-member' ),
'um_user_profile_url_slug_user_login' => __( 'Profile Slug "Username"', 'ultimate-member' ),
'um_user_profile_url_slug_name' => __( 'Profile Slug "First and Last Name with \'.\'"', 'ultimate-member' ),
'um_user_profile_url_slug_name_dash' => __( 'Profile Slug "First and Last Name with \'-\'"', 'ultimate-member' ),
'um_user_profile_url_slug_name_plus' => __( 'Profile Slug "First and Last Name with \'+\'"', 'ultimate-member' ),
'um_user_profile_url_slug_user_id' => __( 'Profile Slug "User ID"', 'ultimate-member' ),
'_um_last_login' => __( 'Last Login Timestamp', 'ultimate-member' ),
//Private content extension
'_um_private_content_post_id' => __( 'Private Content Post ID', 'ultimate-member' ),
//Verified Users extension
'_um_verified' => __( 'Verified Account', 'ultimate-member' ),
//Terms & Conditions extension
'use_terms_conditions_agreement' => __( 'Terms&Conditions Agreement', 'ultimate-member' ),
//GDPR extension
'use_gdpr_agreement' => __( 'Privacy Policy Agreement', 'ultimate-member' ),
);
$all_fields = UM()->builtin()->all_user_fields( null, true );
unset( $all_fields[0] );
$all_fields = array_map( function( $value ) {
return $value['title'];
}, $all_fields );
$this->meta_associations = array_merge( $this->meta_associations, $all_fields );
/**
* UM hook
*
* @type filter
* @title um_gdpr_meta_associations
* @description Exclude taxonomies for UM
* @input_vars
* [{"var":"$meta_associations","type":"array","desc":"Meta Keys Titles"}]
* @change_log
* ["Since: 2.0.14"]
* @usage
* <?php add_filter( 'um_gdpr_meta_associations', 'function_name', 10, 1 ); ?>
* @example
* <?php
* add_filter( 'um_gdpr_meta_associations', 'my_gdpr_meta_associations', 10, 1 );
* function my_gdpr_meta_associations( $meta_associations ) {
* // your code here
* return $meta_associations;
* }
* ?>
*/
$this->meta_associations = apply_filters( 'um_gdpr_meta_associations', $this->meta_associations );
}
/**
* Return the default suggested privacy policy content.
*
* @return string The default policy content.
*/
function plugin_get_default_privacy_content() {
ob_start();
include UM()->admin()->templates_path . 'gdpr.php';
return ob_get_clean();
}
/**
* Add the suggested privacy policy text to the policy postbox.
*/
function plugin_add_suggested_privacy_content() {
$content = $this->plugin_get_default_privacy_content();
wp_add_privacy_policy_content( ultimatemember_plugin_name, $content );
}
/**
* Register exporter for Plugin user data.
*
* @see https://github.com/allendav/wp-privacy-requests/blob/master/EXPORT.md
*
* @param $exporters
*
* @return array
*/
function plugin_register_exporters( $exporters ) {
$exporters[] = array(
'exporter_friendly_name' => ultimatemember_plugin_name,
'callback' => array( &$this, 'data_exporter' )
);
return $exporters;
}
/**
* Get user metadata in key => value array
*
*
* @param $user_id
*
* @return array
*/
function get_metadata( $user_id ) {
global $wpdb;
$metadata = $wpdb->get_results( $wpdb->prepare(
"SELECT meta_key, meta_value
FROM {$wpdb->usermeta}
WHERE user_id = %d",
$user_id
), ARRAY_A );
$filtered = array();
foreach ( $metadata as $data ) {
if ( in_array( $data['meta_key'], array_keys( $this->meta_associations ) ) ) {
$filtered[] = array(
'key' => $data['meta_key'],
'name' => $this->meta_associations[ $data['meta_key'] ],
//'value' => maybe_unserialize( $data['meta_value'] ),
'value' => $data['meta_value'],
);
}
}
return $filtered;
}
/**
* Exporter for Plugin user data.
*
* @see https://github.com/allendav/wp-privacy-requests/blob/master/EXPORT.md
*
* @param $email_address
* @param int $page
*
* @return array
*/
function data_exporter( $email_address, $page = 1 ) {
$export_items = array();
$user = get_user_by( 'email', $email_address );
if ( $user && $user->ID ) {
// Most item IDs should look like postType-postID
// If you don't have a post, comment or other ID to work with,
// use a unique value to avoid having this item's export
// combined in the final report with other items of the same id
$item_id = "ultimate-member-{$user->ID}";
// Core group IDs include 'comments', 'posts', etc.
// But you can add your own group IDs as needed
$group_id = 'ultimate-member';
// Optional group label. Core provides these for core groups.
// If you define your own group, the first exporter to
// include a label will be used as the group label in the
// final exported report
$group_label = ultimatemember_plugin_name;
// Plugins can add as many items in the item data array as they want
//$data = array();
$data = $this->get_metadata( $user->ID );
if ( ! empty( $data ) ) {
// Add this group of items to the exporters data array.
$export_items[] = array(
'group_id' => $group_id,
'group_label' => $group_label,
'item_id' => $item_id,
'data' => $data,
);
}
}
// Returns an array of exported items for this pass, but also a boolean whether this exporter is finished.
//If not it will be called again with $page increased by 1.
return array(
'data' => $export_items,
'done' => true,
);
}
/**
* Register eraser for Plugin user data.
*
* @param array $erasers
*
* @return array
*/
function plugin_register_erasers( $erasers = array() ) {
$erasers[] = array(
'eraser_friendly_name' => ultimatemember_plugin_name,
'callback' => array( &$this, 'data_eraser' )
);
return $erasers;
}
/**
* Eraser for Plugin user data.
*
* @param $email_address
* @param int $page
*
* @return array
*/
function data_eraser( $email_address, $page = 1 ) {
if ( empty( $email_address ) ) {
return array(
'items_removed' => false,
'items_retained' => false,
'messages' => array(),
'done' => true,
);
}
$user = get_user_by( 'email', $email_address );
$messages = array();
$items_removed = false;
$items_retained = false;
if ( $user && $user->ID ) {
$data = $this->get_metadata( $user->ID );
foreach ( $data as $metadata ) {
$deleted = delete_user_meta( $user->ID, $metadata['key'] );
if ( $deleted ) {
$items_removed = true;
} else {
$messages[] = sprintf( __( 'Your %s was unable to be removed at this time.', 'ultimate-member' ), $metadata['name'] );
$items_retained = true;
}
}
}
// Returns an array of exported items for this pass, but also a boolean whether this exporter is finished.
//If not it will be called again with $page increased by 1.
return array(
'items_removed' => $items_removed,
'items_retained' => $items_retained,
'messages' => $messages,
'done' => true,
);
}
}
}
+5 -2
View File
@@ -1197,12 +1197,14 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
do_action( "um_settings_page_" . $current_tab . "_" . $current_subtab . "_before_section" );
$section_fields = $this->get_section_fields( $current_tab, $current_subtab );
$settings_section = $this->render_settings_section( $section_fields, $current_tab, $current_subtab );
/**
* UM hook
*
* @type filter
* @title um_settings_section_{$current_tab}_{$current_subtab}_content
*
* @description Render settings section
* @input_vars
* [{"var":"$content","type":"string","desc":"Section content"},
@@ -1220,7 +1222,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
* ?>
*/
echo apply_filters( 'um_settings_section_' . $current_tab . '_' . $current_subtab . '_content',
$this->render_settings_section( $section_fields, $current_tab, $current_subtab ),
$settings_section,
$section_fields
);
@@ -1258,6 +1260,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
do_action( "um_settings_page_" . $current_tab . "_" . $current_subtab . "_before_section" );
$section_fields = $this->get_section_fields( $current_tab, $current_subtab );
$settings_section = $this->render_settings_section( $section_fields, $current_tab, $current_subtab );
/**
* UM hook
@@ -1281,7 +1284,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
* ?>
*/
echo apply_filters( 'um_settings_section_' . $current_tab . '_' . $current_subtab . '_content',
$this->render_settings_section( $section_fields, $current_tab, $current_subtab ),
$settings_section,
$section_fields
);
?>
@@ -0,0 +1,71 @@
<div class="um-admin-metabox">
<?php
$options = array(
'' => __( 'Select page', 'ultimate-member' )
);
$pages = get_pages();
foreach ( $pages as $page ) {
$options[$page->ID] = $page->post_title;
}
UM()->admin_forms( array(
'class' => 'um-form-register-gdpr um-top-label',
'prefix_id' => 'form',
'fields' => array(
array(
'id' => '_um_register_use_gdpr',
'type' => 'select',
'label' => __( 'Enable on this form', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value( '_um_register_use_gdpr', null, '' ),
'options' => array(
'0' => __( 'No', 'ultimate-member' ),
'1' => __( 'Yes', 'ultimate-member' )
),
),
array(
'id' => '_um_register_use_gdpr_content_id',
'type' => 'select',
'label' => __( 'Content', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value('_um_register_use_gdpr_content_id', null, '' ),
'options' => $options,
'conditional' => array( '_um_register_use_gdpr', '=', '1' )
),
array(
'id' => '_um_register_use_gdpr_toggle_show',
'type' => 'text',
'label' => __( 'Toggle Show text', 'ultimate-member' ),
'placeholder' => __( 'Show privacy policy', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value('_um_register_use_gdpr_toggle_show', null, __( 'Show privacy policy', 'ultimate-member' ) ),
'conditional' => array( '_um_register_use_gdpr', '=', '1' )
),
array(
'id' => '_um_register_use_gdpr_toggle_hide',
'type' => 'text',
'label' => __( 'Toggle Hide text', 'ultimate-member' ),
'placeholder' => __( 'Hide privacy policy', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value('_um_register_use_gdpr_toggle_hide', null, __( 'Hide privacy policy', 'ultimate-member' ) ),
'conditional' => array( '_um_register_use_gdpr', '=', '1' )
),
array(
'id' => '_um_register_use_gdpr_agreement',
'type' => 'text',
'label' => __( 'Checkbox agreement description', 'ultimate-member' ),
'placeholder' => __( 'Please confirm that you agree to our privacy policy', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value('_um_register_use_gdpr_agreement', null, __( 'Please confirm that you agree to our privacy policy', 'ultimate-member' ) ),
'conditional' => array( '_um_register_use_gdpr', '=', '1' )
),
array(
'id' => '_um_register_use_gdpr_error_text',
'type' => 'text',
'label' => __( 'Error Text', 'ultimate-member' ),
'placeholder' => __( 'Please confirm your acceptance of our privacy policy', 'ultimate-member' ),
'value' => UM()->query()->get_meta_value('_um_register_use_gdpr_error_text', null, __( 'Please confirm your acceptance of our privacy policy', 'ultimate-member' ) ),
'conditional' => array( '_um_register_use_gdpr', '=', '1' )
)
)
) )->render_form(); ?>
<div class="um-admin-clear"></div>
</div>
+40
View File
@@ -0,0 +1,40 @@
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly. ?>
<h2>
<?php _e( 'What personal data we collect and why we collect it', 'ultimate-member' ); ?>
</h2>
<h3>
<?php _e( 'Forms', 'ultimate-member' ); ?>
</h3>
<p>
<?php printf( __( '%s provides you with forms for user registration, login and profiles.', 'ultimate-member' ), ultimatemember_plugin_name ); ?>
</p>
<p>
<?php _e( 'Via these forms you are collecting personal data from your users.', 'ultimate-member' ); ?>
</p>
<p>
<?php _e( 'You should include in your privacy policy what personal data is captured when someone submits/fills in one of the forms, why you collect it and what you do with this data and how long you keep it.', 'ultimate-member' ); ?>
</p>
<h2>
<?php _e( 'How long we retain your data', 'ultimate-member' ); ?>
</h2>
<p>
<?php _e( 'Registered user information is retained in your websites database indefinitely.', 'ultimate-member' ); ?>
</p>
<p>
<?php _e( 'Data can be exported or removed upon users request via the existing WordPress data exporter or eraser.', 'ultimate-member' ); ?>
</p>
<p>
<?php _e( 'If syncing data to a 3rd party service (e.g Mailchimp via our MailChimp extension), data is retained there until unsubscribed or deleted.', 'ultimate-member' ); ?>
</p>
<h2>
<?php _e( 'Where we send your data', 'ultimate-member' ); ?>
</h2>
<p>
<?php printf( __( '%s does not send any user data outside of your site by default.', 'ultimate-member' ), ultimatemember_plugin_name ); ?>
</p>
<p>
<?php _e( 'If you have extended the functionality of the plugin (e.g sending registered user data to MailChimp via our MailChimp extension, this user info may be passed to these external services. These services may be located abroad and outwith the EU.', 'ultimate-member' ); ?>
</p>
+45
View File
@@ -485,6 +485,7 @@ if ( ! class_exists( 'UM' ) ) {
$this->ajax_init();
$this->metabox();
$this->admin_upgrade()->init_packages_ajax_handlers();
$this->admin_gdpr();
} elseif ( $this->is_request( 'admin' ) ) {
$this->admin();
$this->admin_menu();
@@ -497,6 +498,7 @@ if ( ! class_exists( 'UM' ) ) {
$this->users();
$this->dragdrop();
$this->plugin_updater();
$this->admin_gdpr();
} elseif ( $this->is_request( 'frontend' ) ) {
$this->enqueue();
$this->account();
@@ -525,6 +527,7 @@ if ( ! class_exists( 'UM' ) ) {
//$this->tracking();
$this->mobile();
$this->external_integrations();
$this->gdpr();
}
@@ -640,6 +643,48 @@ if ( ! class_exists( 'UM' ) ) {
}
/**
* GDPR privacy policy
*
* @since 2.0.14
*
* @return bool|um\admin\core\Admin_GDPR()
*/
function admin_gdpr() {
global $wp_version;
if ( version_compare( $wp_version, '4.9.6', '<' ) ) {
return false;
}
if ( empty( $this->classes['admin_gdpr'] ) ) {
$this->classes['admin_gdpr'] = new um\admin\core\Admin_GDPR();
}
return $this->classes['admin_gdpr'];
}
/**
* GDPR privacy policy
*
* @since 2.0.14
*
* @return bool|um\core\GDPR()
*/
function gdpr() {
global $wp_version;
if ( version_compare( $wp_version, '4.9.6', '<' ) ) {
return false;
}
if ( empty( $this->classes['gdpr'] ) ) {
$this->classes['gdpr'] = new um\core\GDPR();
}
return $this->classes['gdpr'];
}
/**
* @since 2.0
*
+1 -1
View File
@@ -1252,7 +1252,7 @@ if ( ! class_exists( 'um\core\Builtin' ) ) {
*/
$fields_without_metakey = apply_filters( 'um_fields_without_metakey', $fields_without_metakey );
if ( !$show_all ) {
if ( ! $show_all ) {
$this->fields_dropdown = array('image','file','password','rating');
$this->fields_dropdown = array_merge( $this->fields_dropdown, $fields_without_metakey );
} else {
+2
View File
@@ -319,6 +319,8 @@ if ( ! class_exists( 'um\core\Enqueue' ) ) {
wp_register_script('um_functions', um_url . 'assets/js/um-functions' . $this->suffix . '.js', array('jquery', 'jquery-masonry') );
wp_enqueue_script('um_functions');
wp_enqueue_script( 'um-gdpr', um_url . 'assets/js/um-gdpr' . $this->suffix . '.js', array( 'jquery' ), ultimatemember_version, false );
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace um\core;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) exit;
if ( ! class_exists( 'um\core\GDPR' ) ) {
/**
* Class Admin_GDPR
* @package um\core
*/
class GDPR {
/**
* Admin_GDPR constructor.
*/
function __construct() {
add_action( 'um_submit_form_register', array( &$this, 'agreement_validation' ), 9 );
add_filter( 'um_before_save_filter_submitted', array( &$this, 'add_agreement_date' ), 10, 1 );
add_filter( 'um_email_registration_data', array( &$this, 'email_registration_data' ), 10, 1 );
add_action( 'um_after_form_fields', array( &$this, 'display_option' ) );
}
/**
* @param $args
*/
function display_option( $args ) {
if ( isset( $args['use_gdpr'] ) && $args['use_gdpr'] == 1 ) {
require um_path . 'templates/gdpr-register.php';
}
}
/**
* @param $args
*/
function agreement_validation( $args ) {
$gdpr_enabled = get_post_meta( $args['form_id'], '_um_register_use_gdpr', true );
if ( $gdpr_enabled && ! isset( $args['submitted']['use_gdpr_agreement'] ) ) {
UM()->form()->add_error( 'use_gdpr_agreement', isset( $args['use_gdpr_error_text'] ) ? $args['use_gdpr_error_text'] : '' );
}
}
/**
* @param $submitted
*
* @return mixed
*/
function add_agreement_date( $submitted ) {
if ( isset( $submitted['use_gdpr_agreement'] ) ) {
$submitted['use_gdpr_agreement'] = time();
}
return $submitted;
}
/**
* @param $submitted
*
* @return mixed
*/
function email_registration_data( $submitted ) {
$timestamp = ! empty( $submitted['timestamp'] ) ? $submitted['timestamp'] : $submitted['use_gdpr_agreement'];
if ( ! empty( $submitted['use_gdpr_agreement'] ) ) {
$submitted['GDPR Applied'] = date( "d M Y H:i", $timestamp );
unset( $submitted['use_gdpr_agreement'] );
}
return $submitted;
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php ?>
<!-- This file should primarily consist of HTML with a little bit of PHP. -->
<div class="um-field um-field-type_terms_conditions" data-key="use_terms_conditions_agreement" style="display:block;padding:0;">
<div class="um-field-area">
<div class='um-gdpr-content' style="display:none;">
<?php if ( ! empty( $args['use_gdpr_content_id'] ) ) {
$um_content_query = get_post( $args['use_gdpr_content_id'] );
if ( isset( $um_content_query ) ) {
echo apply_filters( 'um_gdpr_policies_page_content', $um_content_query->post_content, $args );
}
} ?>
</div>
<a href="javascript:;" class="um-toggle-gdpr" data-toggle-state="hidden"
data-toggle-show="<?php echo ! empty( $args['use_gdpr_toggle_show'] ) ? $args['use_gdpr_toggle_show'] : __( 'Show privacy policy', 'ultimate-member' ); ?>"
data-toggle-hide="<?php echo ! empty( $args['use_gdpr_toggle_hide'] ) ? $args['use_gdpr_toggle_hide'] : __( 'Hide privacy policy', 'ultimate-member' ); ?>">
<?php echo ! empty( $args['use_gdpr_toggle_show'] ) ? $args['use_gdpr_toggle_show'] : __( 'Show privacy policy', 'ultimate-member' ); ?>
</a>
</div>
<div class="um-field-area">
<label class="um-field-checkbox">
<input type="checkbox" name="use_gdpr_agreement" value="1">
<span class="um-field-checkbox-state">
<i class="um-icon-android-checkbox-outline-blank"></i>
</span>
<span class="um-field-checkbox-option">
<?php echo ! empty( $args['use_gdpr_agreement'] ) ? $args['use_gdpr_agreement'] : __( 'Please confirm that you agree to our privacy policy', 'ultimate-member' ); ?>
</span>
</label>
<div class="um-clear"></div>
<?php $errors = UM()->form()->errors;
if ( isset( $errors['use_gdpr_agreement'] ) ) {
$error_message = ! empty( $args['use_gdpr_error_text'] ) ? $args['use_gdpr_error_text'] : __( 'Please confirm your acceptance of our privacy policy', 'ultimate-member' );
echo '<p class="um-notice err"><i class="um-icon-ios-close-empty" onclick="jQuery(this).parent().fadeOut();"></i>' . $error_message . '</p><br/>';
} ?>
<div class="um-clear"></div>
</div>
</div>
+1 -1
View File
@@ -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.0.13
Version: 2.0.14
Author: Ultimate Member
Author URI: http://ultimatemember.com/
Text Domain: ultimate-member