<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][12882] trunk/src/bp-xprofile: xProfile: introduce the Checkbox Acceptance field type</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://buddypress.trac.wordpress.org/changeset/12882">12882</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/12882","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>imath</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-04-13 04:26:05 +0000 (Tue, 13 Apr 2021)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>xProfile: introduce the Checkbox Acceptance field type

This new field type helps community site administrators get members agreement about specific rules. It can be terms of service, code of conduct, game rules, etc.

When adding such a field, the xProfile field Admin screen will contain a specific metabox to select the WordPress page to use as the linked content needing members agreement.

This field can be required or not, but cannot be updated once members agreed to the WordPress page it relies to.

Props mahdiar, vapvarun

Fixes <a href="http://buddypress.trac.wordpress.org/ticket/4747">#4747</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcbpxprofilebpxprofilefunctionsphp">trunk/src/bp-xprofile/bp-xprofile-functions.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcbpxprofileclassesclassbpxprofilefieldtypecheckboxacceptancephp">trunk/src/bp-xprofile/classes/class-bp-xprofile-field-type-checkbox-acceptance.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpxprofilebpxprofilefunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-xprofile/bp-xprofile-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-xprofile/bp-xprofile-functions.php   2021-04-13 04:16:04 UTC (rev 12881)
+++ trunk/src/bp-xprofile/bp-xprofile-functions.php     2021-04-13 04:26:05 UTC (rev 12882)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -149,18 +149,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function bp_xprofile_get_field_types() {
</span><span class="cx" style="display: block; padding: 0 10px">        $fields = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                'checkbox'       => 'BP_XProfile_Field_Type_Checkbox',
-               'datebox'        => 'BP_XProfile_Field_Type_Datebox',
-               'multiselectbox' => 'BP_XProfile_Field_Type_Multiselectbox',
-               'number'         => 'BP_XProfile_Field_Type_Number',
-               'url'            => 'BP_XProfile_Field_Type_URL',
-               'radio'          => 'BP_XProfile_Field_Type_Radiobutton',
-               'selectbox'      => 'BP_XProfile_Field_Type_Selectbox',
-               'textarea'       => 'BP_XProfile_Field_Type_Textarea',
-               'textbox'        => 'BP_XProfile_Field_Type_Textbox',
-               'telephone'      => 'BP_XProfile_Field_Type_Telephone',
-               'wp-biography'   => 'BP_XProfile_Field_Type_WordPress_Biography',
-               'wp-textbox'     => 'BP_XProfile_Field_Type_WordPress_Textbox',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         'checkbox'            => 'BP_XProfile_Field_Type_Checkbox',
+               'datebox'             => 'BP_XProfile_Field_Type_Datebox',
+               'multiselectbox'      => 'BP_XProfile_Field_Type_Multiselectbox',
+               'number'              => 'BP_XProfile_Field_Type_Number',
+               'url'                 => 'BP_XProfile_Field_Type_URL',
+               'radio'               => 'BP_XProfile_Field_Type_Radiobutton',
+               'selectbox'           => 'BP_XProfile_Field_Type_Selectbox',
+               'textarea'            => 'BP_XProfile_Field_Type_Textarea',
+               'textbox'             => 'BP_XProfile_Field_Type_Textbox',
+               'telephone'           => 'BP_XProfile_Field_Type_Telephone',
+               'wp-biography'        => 'BP_XProfile_Field_Type_WordPress_Biography',
+               'wp-textbox'          => 'BP_XProfile_Field_Type_WordPress_Textbox',
+               'checkbox_acceptance' => 'BP_XProfile_Field_Type_Checkbox_Acceptance',
</ins><span class="cx" style="display: block; padding: 0 10px">         );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcbpxprofileclassesclassbpxprofilefieldtypecheckboxacceptancephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/bp-xprofile/classes/class-bp-xprofile-field-type-checkbox-acceptance.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-xprofile/classes/class-bp-xprofile-field-type-checkbox-acceptance.php                                (rev 0)
+++ trunk/src/bp-xprofile/classes/class-bp-xprofile-field-type-checkbox-acceptance.php  2021-04-13 04:26:05 UTC (rev 12882)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,382 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * BuddyPress XProfile Classes.
+ *
+ * @package BuddyPress
+ * @subpackage XProfileClasses
+ * @since 8.0.0
+ */
+
+// Exit if accessed directly.
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Checkbox Acceptance xProfile field type.
+ *
+ * @since 8.0.0
+ */
+class BP_XProfile_Field_Type_Checkbox_Acceptance extends BP_XProfile_Field_Type {
+
+       /**
+        * Checkbox Acceptance field's visibility setting.
+        *
+        * Defaults to 'adminsonly'. This property enforces Field's default visibility.
+        *
+        * @since 8.0.0
+        *
+        * @return string The Checkbox Acceptance field's visibility setting.
+        */
+       public $visibility = 'adminsonly';
+
+       /**
+        * Supported features for the Checkbox Acceptance field type.
+        *
+        * @since 8.0.0
+        * @var bool[] The WordPress field supported features.
+        */
+       public static $supported_features = array(
+               'switch_fieldtype'        => false,
+               'required'                => true,
+               'do_autolink'             => false,
+               'allow_custom_visibility' => false,
+               'member_types'            => false,
+       );
+
+       /**
+        * Constructor for the Checkbox Acceptance field type.
+        *
+        * @since 8.0.0
+        */
+       public function __construct() {
+               parent::__construct();
+
+               $this->name     = _x( 'Checkbox Acceptance', 'xprofile field type', 'buddypress' );
+               $this->category = _x( 'Single Fields', 'xprofile field type category', 'buddypress' );
+
+               $this->supports_options    = false;
+               $this->do_settings_section = true;
+               $this->accepts_null_value  = false;
+
+               $this->set_format( '/^.+$/', 'replace' );
+
+               /**
+                * Fires inside __construct() method for bp_xprofile_field_type_checkbox_acceptance class.
+                *
+                * @since 8.0.0
+                *
+                * @param BP_XProfile_Field_Type_Checkbox_Acceptance $this Current instance of the Checkbox Acceptance field type.
+                */
+               do_action( 'bp_xprofile_field_type_checkbox_acceptance', $this );
+
+               // Make sure it's not possible to edit an accepted Checkbox Acceptance field.
+               add_filter( 'bp_xprofile_set_field_data_pre_validate', array( $this, 'enforce_field_value' ), 10, 2 );
+       }
+
+
+       /**
+        * Output the edit field HTML for this field type.
+        *
+        * Must be used inside the {@link bp_profile_fields()} template loop.
+        *
+        * @since 8.0.0
+        *
+        * @param array $raw_properties Optional key/value array of
+        * {@link http://dev.w3.org/html5/markup/textarea.html permitted attributes}
+        *  that you want to add.
+        */
+       public function edit_field_html( array $raw_properties = array() ) {
+               $user_id   = bp_displayed_user_id();
+               $required  = false;
+               $default_r = array();
+
+               if ( isset( $raw_properties['user_id'] ) ) {
+                       $user_id = (int) $raw_properties['user_id'];
+                       unset( $raw_properties['user_id'] );
+               }
+
+               if ( bp_get_the_profile_field_is_required() ) {
+                       $default_r['required'] = 'required'; // HTML5 required attribute.
+                       $required              = true;
+               }
+
+               $r = bp_parse_args(
+                       $raw_properties,
+                       $default_r,
+                       'checkbox_acceptance'
+               );
+               ?>
+               <legend>
+                       <?php bp_the_profile_field_name(); ?>
+                       <?php bp_the_profile_field_required_label(); ?>
+               </legend>
+
+               <?php
+               /** This action is documented in bp-xprofile/bp-xprofile-classes */
+               do_action( bp_get_the_profile_field_errors_action() );
+
+               $r['user_id'] = $user_id;
+               bp_the_profile_field_options( $r );
+               ?>
+
+               <?php if ( bp_get_the_profile_field_description() ) : ?>
+                       <p class="description" tabindex="0"><?php bp_the_profile_field_description(); ?></p>
+               <?php endif;
+       }
+
+       /**
+        * Field html for Admin-> User->Profile Fields screen.
+        *
+        * @since 8.0.0
+        *
+        * @param array $raw_properties properties.
+        */
+       public function admin_field_html( array $raw_properties = array() ) {
+               $page_id   = bp_xprofile_get_meta( bp_get_the_profile_field_id(), 'field', 'bp_xprofile_checkbox_acceptance_page', true );
+               $page      = null;
+               $default_r = array( 'type' => 'checkbox' );
+
+               if ( bp_get_the_profile_field_is_required() ) {
+                       $default_r['required'] = 'required'; // HTML5 required attribute.
+               }
+
+               $r = bp_parse_args(
+                       $raw_properties,
+                       $default_r,
+                       'checkbox_acceptance'
+               );
+
+               if ( $page_id ) {
+                       $page = get_post( $page_id );
+               }
+               ?>
+
+               <?php if ( $page instanceof WP_Post ) : ?>
+                       <label for="<?php bp_the_profile_field_input_name(); ?>">
+                               <input <?php echo $this->get_edit_field_html_elements( $r ); ?>>
+                               <?php
+                               printf(
+                                       /* translators: %s: link to the page the user needs to accept the terms of. */
+                                       esc_html__( 'I agree to %s.', 'buddypress' ),
+                                       '<a href="' . esc_url( get_permalink( $page ) ) . '">' . esc_html( get_the_title( $page ) ) . '</a>'
+                               );
+                               ?>
+                       </label>
+               <?php endif;
+       }
+
+       /**
+        * Admin new field screen.
+        *
+        * @since 8.0.0
+        *
+        * @param BP_XProfile_Field $current_field Profile field object.
+        * @param string            $control_type  Control type.
+        */
+       public function admin_new_field_html( BP_XProfile_Field $current_field, $control_type = '' ) {
+               $type = array_search( get_class( $this ), bp_xprofile_get_field_types() );
+
+               if ( false === $type ) {
+                       return;
+               }
+
+               $class   = $current_field->type != $type ? 'display: none;' : '';
+               $page_id = bp_xprofile_get_meta( $current_field->id, 'field', 'bp_xprofile_checkbox_acceptance_page', true );
+               ?>
+
+               <div id="<?php echo esc_attr( $type ); ?>" class="postbox bp-options-box" style="<?php echo esc_attr( $class ); ?> margin-top: 15px;">
+                       <h3><?php esc_html_e( 'Select the page the user needs to accept the terms of:', 'buddypress' ); ?></h3>
+                       <div class="inside">
+                               <p>
+                                       <?php
+                                       echo wp_dropdown_pages(
+                                               array(
+                                                       'name'             => 'bp_xprofile_checkbox_acceptance_page',
+                                                       'echo'             => false,
+                                                       'show_option_none' => __( '&mdash; Select &mdash;', 'buddypress' ),
+                                                       'selected'         => $page_id ? $page_id : false,
+                                               )
+                                       );
+
+                                       $page = null;
+                                       if ( $page_id ) {
+                                               $page = get_post( $page_id );
+                                       }
+                                       ?>
+
+                                       <?php if ( $page instanceof WP_Post ) : ?>
+
+                                               <a href="<?php echo esc_url( get_permalink( $page ) ); ?>" class="button-secondary" target="_bp">
+                                                       <?php esc_html_e( 'View', 'buddypress' ); ?> <span class="dashicons dashicons-external" aria-hidden="true" style="vertical-align: text-bottom;"></span>
+                                                       <span class="screen-reader-text"><?php esc_html_e( '(opens in a new tab)', 'buddypress' ); ?></span>
+                                               </a>
+
+                                       <?php endif; ?>
+                               </p>
+                       </div>
+               </div>
+               <?php
+       }
+
+       /**
+        * Save settings from the field edit screen in the Dashboard.
+        *
+        * @since 8.0.0
+        *
+        * @param int   $field_id ID of the field.
+        * @param array $settings Array of settings.
+        * @return bool True on success.
+        */
+       public function admin_save_settings( $field_id, $settings ) {
+               if ( isset( $_POST['bp_xprofile_checkbox_acceptance_page'] ) ) {
+                       bp_xprofile_update_meta( $field_id, 'field', 'bp_xprofile_checkbox_acceptance_page', absint( wp_unslash( $_POST['bp_xprofile_checkbox_acceptance_page'] ) ) );
+               }
+
+               return true;
+       }
+
+       /**
+        * Profile edit/register options html.
+        *
+        * @since 8.0.0
+        *
+        * @param array $args args.
+        */
+       public function edit_field_options_html( array $args = array() ) {
+               $field_id            = (int) $this->field_obj->id;
+               $params              = wp_parse_args( $args, array( 'user_id' => bp_displayed_user_id() ) );
+               $checkbox_acceptance = (int) maybe_unserialize( \BP_XProfile_ProfileData::get_value_byid( $field_id, $params['user_id'] ) );
+
+               if ( ! empty( $_POST[ 'field_' . $field_id ] ) ) {
+                       $new_checkbox_acceptance = (int) wp_unslash( $_POST[ 'field_' . $field_id ] );
+
+                       if ( $checkbox_acceptance !== $new_checkbox_acceptance ) {
+                               $checkbox_acceptance = $new_checkbox_acceptance;
+                       }
+               }
+
+               $r = array(
+                       'type'     => 'checkbox',
+                       'name'     => bp_get_the_profile_field_input_name(),
+                       'id'       => bp_get_the_profile_field_input_name(),
+                       'value'    => 1,
+                       'class'    => 'checkbox-acceptance',
+               );
+
+               if ( bp_get_the_profile_field_is_required() ) {
+                       $r['required'] = 'required'; // HTML5 required attribute.
+               }
+
+               if ( 1 === $checkbox_acceptance ) {
+                       $r['checked']  = 'checked';
+                       $r['readonly'] = 'readonly';
+                       $r['onclick']  = 'return false;';
+               }
+
+               $page_id = bp_xprofile_get_meta( $field_id, 'field', 'bp_xprofile_checkbox_acceptance_page', true );
+               $page    = null;
+               $html    = '';
+
+               if ( $page_id ) {
+                       $page = get_post( $page_id );
+               }
+
+               if ( $page instanceof WP_Post ) {
+                       $html = sprintf(
+                               '<div class="bp-xprofile-checkbox-acceptance-field"><input %1$s />%2$s</div>',
+                               $this->get_edit_field_html_elements( $r ),
+                               sprintf(
+                                       /* translators: %s: link to the page the user needs to accept the terms of. */
+                                       esc_html__( 'I agree to %s.', 'buddypress' ),
+                                       '<a href="' . esc_url( get_permalink( $page ) ) . '">' . esc_html( get_the_title( $page ) ) . '</a>'
+                               )
+                       );
+               }
+
+               /**
+                * Filter here to edit the HTML output.
+                *
+                * @since 8.0.0
+                *
+                * @param string $html                The HTML output.
+                * @param int    $field_id            The field ID.
+                * @param array  $r                   The edit field HTML elements data.
+                * @param int    $checkbox_acceptance The field value.
+                */
+               echo apply_filters( 'bp_get_the_profile_field_checkbox_acceptance', $html, $field_id, $checkbox_acceptance );
+       }
+
+       /**
+        * Enforces the field value if it has been already accepted.
+        *
+        * As it's always possible to edit HTML source and remove the `readonly="readonly"` attribute
+        * of the checkbox, we may need to enforce the field value.
+        *
+        * @since 8.0.0
+        *
+        * @param mixed             $value Value passed to xprofile_set_field_data().
+        * @param BP_XProfile_Field $field Field object.
+        * @return mixed The field value.
+        */
+       public function enforce_field_value( $value, BP_XProfile_Field $field ) {
+               if ( 'checkbox_acceptance' === $field->type && 1 !== (int) $value && 1 === (int) $field->data->value ) {
+                       $value = 1;
+               }
+
+               return $value;
+       }
+
+       /**
+        * Check if field is valid?
+        *
+        * @since 8.0.0
+        *
+        * @param string|int $values value.
+        * @return bool
+        */
+       public function is_valid( $value ) {
+               if ( empty( $value ) || 1 === (int) $value ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Modify the appearance of value.
+        *
+        * @since 8.0.0
+        *
+        * @param string $field_value Original value of field.
+        * @param int    $field_id field id.
+        *
+        * @return string   Value formatted
+        */
+       public static function display_filter( $field_value, $field_id = 0 ) {
+               $page_id = bp_xprofile_get_meta( $field_id, 'field', 'bp_xprofile_checkbox_acceptance_page', true );
+               $page    = null;
+               $html    = esc_html__( 'No', 'buddypress' );
+
+               /* translators: %s: link to the page the user needs to accept the terms of. */
+               $acceptance_text = esc_html__( 'I did not agree to %s', 'buddypress' );
+
+               if ( $page_id ) {
+                       $page = get_post( $page_id );
+               }
+
+               if ( ! empty( $field_value ) ) {
+                       $html = esc_html__( 'Yes', 'buddypress' );
+
+                       /* translators: %s: link to the page the user needs to accept the terms of. */
+                       $acceptance_text = esc_html__( 'I agreed to %s.', 'buddypress' );
+               }
+
+               if ( $page instanceof WP_Post ) {
+                       $html = sprintf(
+                               $acceptance_text,
+                               '<a href="' . esc_url( get_permalink( $page ) ) . '">' . esc_html( get_the_title( $page ) ) . '</a>'
+                       );
+               }
+
+               return $html;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/bp-xprofile/classes/class-bp-xprofile-field-type-checkbox-acceptance.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span></div>

</body>
</html>