Make WordPress Core

Changeset 43101


Ignore:
Timestamp:
05/02/2018 02:58:12 AM (8 years ago)
Author:
SergeyBiryukov
Message:

Privacy: add a postbox that is shown when editing the privacy policy page, and where plugins and core will output suggested content and additional privacy info. First run.

Props melchoyce, azaozz.
Merges [42980] to the 4.9 branch.
See #43620.

Location:
branches/4.9
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • branches/4.9

  • branches/4.9/src/wp-admin/css/common.css

    r42883 r43101  
    29822982.sidebar-name .toggle-indicator:before,
    29832983.js .meta-box-sortables .postbox .toggle-indicator:before,
    2984 .bulk-action-notice .toggle-indicator:before {
     2984.bulk-action-notice .toggle-indicator:before,
     2985.privacy-text-box .toggle-indicator:before {
    29852986        content: "\f142";
    29862987        display: inline-block;
     
    29942995.js .widgets-holder-wrap.closed .toggle-indicator:before,
    29952996.js .meta-box-sortables .postbox.closed .handlediv .toggle-indicator:before,
    2996 .bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before {
     2997.bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before,
     2998.privacy-text-box.closed .toggle-indicator:before {
    29972999        content: "\f140";
    29983000}
  • branches/4.9/src/wp-admin/css/edit.css

    r41859 r43101  
    645645.edit-form-section {
    646646        margin-bottom: 20px;
     647}
     648
     649/* Sugested text for privacy policy postbox */
     650.privacy-text-box {
     651        margin: 10px 0 0;
     652       
     653}
     654
     655.privacy-text-box-head {
     656        border-left: 4px solid #ffb900;
     657        border-bottom: 1px solid #e5e5e5;
     658        box-shadow: 0 1px 1px rgba(0,0,0,0.04);
     659       
     660}
     661
     662.privacy-text-box .privacy-text-box-head.hndle {
     663        cursor: pointer;
     664}
     665
     666.privacy-text-box-head p {
     667        padding: 0 12px 14px;
     668        margin: 0;
     669}
     670
     671.privacy-text-box-body {
     672        height: 320px;
     673        overflow: auto;
     674       
     675}
     676
     677#privacy-text-box .inside {
     678        margin: 0;
     679}
     680
     681.privacy-text-box h3 {
     682        font-size: 1em;
     683        margin: 1em 0;
     684}
     685
     686.privacy-text-section .privacy-text-copy-button {
     687        float: right;
     688        margin-top: -1.8em;
     689}
     690
     691.privacy-text-section {
     692        padding: 1px 14px 1px 12px;
     693        border-top: 1px solid #e3e3e3;
     694        border-left: 4px solid transparent;
     695}
     696
     697.privacy-text-section.text-updated {
     698        border-left-color: #46b450;
     699        background-color: #ecf7ed;
     700}
     701
     702.privacy-text-section.text-removed {
     703        border-left-color: #dc3232;
     704        background-color: #fbeaea;
     705}
     706
     707.closed .privacy-text-box-body {
     708        display: none;
    647709}
    648710
  • branches/4.9/src/wp-admin/includes/admin-filters.php

    r43092 r43101  
    136136add_filter( 'wp_privacy_personal_data_export_page', 'wp_privacy_process_personal_data_export_page', 10, 6 );
    137137add_action( 'wp_privacy_personal_data_export_file', 'wp_privacy_generate_personal_data_export_file', 10 );
     138
     139// Privacy policy text changes check.
     140add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'text_change_check' ), 20 );
     141
     142// Show a "postbox" with the text suggestions for a privacy policy.
     143add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'privacy_policy_postbox' ) );
     144
     145// Add the suggested policy text from WordPress.
     146add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'add_suggested_content' ), 15 );
     147
     148// Stop checking for text changes after the policy page is updated.
     149add_action( 'post_updated', array( 'WP_Privacy_Policy_Content', '_policy_page_updated' ) );
     150
  • branches/4.9/src/wp-admin/includes/misc.php

    r42831 r43101  
    12141214        }
    12151215}
     1216
     1217/**
     1218 * WP_Privacy_Policy_Content class.
     1219 * TODO: move this to a new file.
     1220 *
     1221 * @since 4.9.6
     1222 */
     1223final class WP_Privacy_Policy_Content {
     1224
     1225        private static $policy_content = array();
     1226
     1227        /**
     1228         * Constructor
     1229         *
     1230         * @since 4.9.6
     1231         */
     1232        private function __construct() {}
     1233
     1234        /**
     1235         * Add privacy information to the postbox shown when editing the privacy policy.
     1236         *
     1237         * Intended for use from `wp_add_privacy_policy_content()`.
     1238         *
     1239         * $since 5.0.0
     1240         *
     1241         * @param string $plugin_name The plugin'as name. Will be shown in the privacy policy metabox.
     1242         * @param string $policy_text The content that should appear in the site's privacy policy.
     1243         */
     1244        public static function add( $plugin_name, $policy_text ) {
     1245                if ( empty( $plugin_name ) || empty( $policy_text ) ) {
     1246                        return;
     1247                }
     1248
     1249                $data = array(
     1250                        'plugin_name' => $plugin_name,
     1251                        'policy_text' => $policy_text,
     1252                );
     1253
     1254                if ( ! in_array( $data, self::$policy_content, true ) ) {
     1255                        self::$policy_content[] = $data;
     1256                }
     1257        }
     1258
     1259        /**
     1260         * Quick check if any privacy info has changed.
     1261         *
     1262         * @since 4.9.6
     1263         */
     1264        public static function text_change_check() {
     1265
     1266                $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     1267
     1268                // The site doesn't have a privacy policy.
     1269                if ( empty( $policy_page_id ) ) {
     1270                        return;
     1271                }
     1272
     1273                if ( ! current_user_can( 'edit_post', $policy_page_id ) ) {
     1274                        return;
     1275                }
     1276
     1277                // Also run when the option doesn't exist yet.
     1278                if ( get_option( '_wp_privacy_text_change_check' ) === 'no-check' ) {
     1279                        return;
     1280                }
     1281
     1282                $old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
     1283                $new = self::$policy_content;
     1284
     1285                // Remove the extra values added to the meta.
     1286                foreach ( $old as $key => $data ) {
     1287                        $old[ $key ] = array(
     1288                                'plugin_name' => $data['plugin_name'],
     1289                                'policy_text' => $data['policy_text'],
     1290                        );
     1291                }
     1292
     1293                // The == operator (equal, not identical) was used intentionally.
     1294                // See http://php.net/manual/en/language.operators.array.php
     1295                if ( $new != $old ) {
     1296                        // A plugin was activated or deactivated, or some policy text has changed.
     1297                        // Show a notice on all screens in wp-admin.
     1298                        add_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'policy_text_changed_notice' ) );
     1299                } else {
     1300                        // Stop checking.
     1301                        update_option( '_wp_privacy_text_change_check', 'no-check' );
     1302                }
     1303        }
     1304
     1305        /**
     1306         * Output an admin notice when some privacy info has changed.
     1307         *
     1308         * @since 4.9.6
     1309         */
     1310        public static function policy_text_changed_notice() {
     1311                global $post;
     1312                $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     1313
     1314                ?>
     1315                <div class="policy-text-updated notice notice-warning is-dismissible">
     1316                        <p><?php
     1317
     1318                                _e( 'The suggested privacy policy text has changed.' );
     1319
     1320                                if ( empty( $post ) || $post->ID != $policy_page_id ) {
     1321                                        ?>
     1322                                        <a href="<?php echo get_edit_post_link( $policy_page_id ); ?>"><?php _e( 'Edit the privacy policy.' ); ?></a>
     1323                                        <?php
     1324                                }
     1325
     1326                        ?></p>
     1327                </div>
     1328                <?php
     1329        }
     1330
     1331        /**
     1332         * Stop checking for changed privacy info when the policy page is updated.
     1333         *
     1334         * @since 4.9.6
     1335         * @access private
     1336         */
     1337        public static function _policy_page_updated( $post_id ) {
     1338                $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     1339
     1340                if ( ! $policy_page_id || $policy_page_id !== (int) $post_id ) {
     1341                        return;
     1342                }
     1343
     1344                // The policy page was updated.
     1345                // Stop checking for text changes.
     1346                update_option( '_wp_privacy_text_change_check', 'no-check' );
     1347
     1348                // Remove updated|removed status.
     1349                $old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
     1350                $done = array();
     1351                $update_cache = false;
     1352
     1353                foreach ( $old as $old_key => $old_data ) {
     1354                        if ( ! empty( $old_data['removed'] ) ) {
     1355                                // Remove the old policy text.
     1356                                $update_cache = true;
     1357                                continue;
     1358                        }
     1359
     1360                        if ( ! empty( $old_data['updated'] ) ) {
     1361                                // 'updated' is now 'added'.
     1362                                $done[] = array(
     1363                                        'plugin_name' => $old_data['plugin_name'],
     1364                                        'policy_text' => $old_data['policy_text'],
     1365                                        'added'       => $old_data['updated'],
     1366                                );
     1367                                $update_cache = true;
     1368                        } else {
     1369                                $done[] = $old_data;
     1370                        }
     1371                }
     1372
     1373                if ( $update_cache ) {
     1374                        delete_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
     1375                        // Update the cache.
     1376                        foreach ( $done as $data ) {
     1377                                add_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content', $data );
     1378                        }
     1379                }
     1380        }
     1381
     1382        /**
     1383         * Check for updated, added or removed privacy policy information from plugins.
     1384         *
     1385         * Caches the current info in post_meta of the policy page.
     1386         *
     1387         * @since 4.9.6
     1388         *
     1389         * @return array The privacy policy text/informtion added by core and plugins.
     1390         */
     1391        public static function get_suggested_policy_text() {
     1392                $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     1393                $new = self::$policy_content;
     1394                $old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
     1395                $checked = array();
     1396                $time = time();
     1397                $update_cache = false;
     1398
     1399                // Check for no-changes and updates.
     1400                foreach ( $new as $new_key => $new_data ) {
     1401                        foreach ( $old as $old_key => $old_data ) {
     1402                                $found = false;
     1403
     1404                                if ( $new_data['policy_text'] === $old_data['policy_text'] ) {
     1405                                        // Use the new plugin name in case it was changed, translated, etc.
     1406                                        if ( $old_data['plugin_name'] !== $new_data['plugin_name'] ) {
     1407                                                $old_data['plugin_name'] = $new_data['plugin_name'];
     1408                                                $update_cache = true;
     1409                                        }
     1410
     1411                                        // A plugin was re-activated.
     1412                                        if ( ! empty( $old_data['removed'] ) ) {
     1413                                                unset( $old_data['removed'] );
     1414                                                $old_data['added'] = $time;
     1415                                                $update_cache = true;
     1416                                        }
     1417
     1418                                        $checked[] = $old_data;
     1419                                        $found = true;
     1420                                } elseif ( $new_data['plugin_name'] === $old_data['plugin_name'] ) {
     1421                                        // The info for the policy was updated.
     1422                                        $checked[] = array(
     1423                                                'plugin_name' => $new_data['plugin_name'],
     1424                                                'policy_text' => $new_data['policy_text'],
     1425                                                'updated'     => $time,
     1426                                        );
     1427                                        $found = $update_cache = true;
     1428                                }
     1429
     1430                                if ( $found ) {
     1431                                        unset( $new[ $new_key ], $old[ $old_key ] );
     1432                                        continue 2;
     1433                                }
     1434                        }
     1435                }
     1436
     1437                if ( ! empty( $new ) ) {
     1438                        // A plugin was activated.
     1439                        foreach ( $new as $new_data ) {
     1440                                if ( ! empty( $new_data['plugin_name'] ) && ! empty( $new_data['policy_text'] ) ) {
     1441                                        $new_data['added'] = $time;
     1442                                        array_unshift( $checked, $new_data );
     1443                                }
     1444                        }
     1445                        $update_cache = true;
     1446                }
     1447
     1448                if ( ! empty( $old ) ) {
     1449                        // A plugin was deactivated.
     1450                        foreach ( $old as $old_data ) {
     1451                                if ( ! empty( $old_data['plugin_name'] ) && ! empty( $old_data['policy_text'] ) ) {
     1452                                        $data = array(
     1453                                                'plugin_name' => $old_data['plugin_name'],
     1454                                                'policy_text' => $old_data['policy_text'],
     1455                                                'removed'     => $time,
     1456                                        );
     1457                                        array_unshift( $checked, $data );
     1458                                }
     1459                        }
     1460                        $update_cache = true;
     1461                }
     1462
     1463                if ( $update_cache ) {
     1464                        delete_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
     1465                        // Update the cache.
     1466                        foreach ( $checked as $data ) {
     1467                                add_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content', $data );
     1468                        }
     1469                }
     1470
     1471                // Stop checking for changes after the postbox has been loaded.
     1472                // TODO make this user removable?
     1473                if ( get_option( '_wp_privacy_text_change_check' ) !== 'no-check' ) {
     1474                        update_option( '_wp_privacy_text_change_check', 'no-check' );
     1475                }
     1476
     1477                return $checked;
     1478        }
     1479
     1480        /**
     1481         * Output the postbox when editing the privacy policy page
     1482         *
     1483         * @since 4.9.6
     1484         *
     1485         * @param $post WP_Post The currently edited post.
     1486         */
     1487        public static function privacy_policy_postbox( $post ) {
     1488                if ( ! ( $post instanceof WP_Post ) ) {
     1489                        return;
     1490                }
     1491
     1492                $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     1493
     1494                if ( ! $policy_page_id || $policy_page_id != $post->ID ) {
     1495                        return;
     1496                }
     1497
     1498                $content_array = self::get_suggested_policy_text();
     1499
     1500                $content = '';
     1501                $date_format = get_option( 'date_format' );
     1502                $copy = __( 'Copy' );
     1503
     1504                foreach ( $content_array as $section ) {
     1505                        $class = $meta = '';
     1506
     1507                        if ( ! empty( $section['removed'] ) ) {
     1508                                $class = ' text-removed';
     1509                                $date = date_i18n( $date_format, $section['removed'] );
     1510                                $meta  = sprintf( __( 'Policy text removed %s.' ), $date );
     1511                        } elseif ( ! empty( $section['updated'] ) ) {
     1512                                $class = ' text-updated';
     1513                                $date = date_i18n( $date_format, $section['updated'] );
     1514                                $meta  = sprintf( __( 'Policy text last updated %s.' ), $date );
     1515                        } elseif ( ! empty( $section['added'] ) ) {
     1516                                $class = ' text-added';
     1517                                $date = date_i18n( $date_format, $section['added'] );
     1518                                $meta  = sprintf( __( 'Policy text added %s.' ), $date );
     1519                        }
     1520
     1521                        $content .= '<div class="privacy-text-section' . $class . '">';
     1522                        $content .= '<h3>' . $section['plugin_name'] . '</h3>';
     1523                        $content .= '<button type="button" class="privacy-text-copy-button button">';
     1524                        $content .= $copy;
     1525                        $content .= '<span class="screen-reader-text">' . sprintf( __( 'Copy suggested policy text from %s.' ), $section['plugin_name'] ) . '</span>';
     1526                        $content .= '</button>';
     1527
     1528                        if ( ! empty( $meta ) ) {
     1529                                $content .= '<span class="privacy-text-meta">' . $meta . '</span>';
     1530                        }
     1531
     1532                        $content .= '<div class="policy-text">' . $section['policy_text'] . '</div>';
     1533                        $content .= "</div>\n";
     1534                }
     1535
     1536                ?>
     1537                <div id="privacy-text-box" class="privacy-text-box postbox <?php echo postbox_classes( 'privacy-text-box', 'page' ); ?>">
     1538                        <button type="button" class="handlediv" aria-expanded="true">
     1539                                <span class="screen-reader-text"><?php _e( 'Toggle panel: Suggested privacy policy text' ); ?></span>
     1540                                <span class="toggle-indicator" aria-hidden="true"></span>
     1541                        </button>
     1542                        <div class="privacy-text-box-head hndle">
     1543                                <h2><?php _e( 'This suggested privacy policy text comes from plugins you have installed.' ); ?></h2>
     1544                                <p>
     1545                                        <?php _e( 'We suggest reviewing this text then copying and pasting it into your privacy policy page.' ); ?>
     1546                                        <?php _e( 'Please remember you are responsible for the policies you choose to adopt, so review the content and make any necessary edits.' ); ?>
     1547                                </p>
     1548                        </div>
     1549
     1550                        <div class="privacy-text-box-body">
     1551                                <?php echo $content; ?>
     1552                        </div>
     1553                </div>
     1554                <?php
     1555        }
     1556
     1557        /**
     1558         * Return the default suggested privacy policy content.
     1559         *
     1560         * @since 4.9.6         
     1561         *
     1562         * @return string The defauld policy content.
     1563         */
     1564        public static function get_default_content() {
     1565                $content  = '<p>' . __( 'Lorem ipsum dolor sit amet consectetuer id elit enim neque est. Sodales tincidunt Nulla leo penatibus Vestibulum adipiscing est cursus Nam Vestibulum. Orci Vivamus mollis eget pretium dictumst Donec Integer auctor sociis rutrum. Mauris felis Donec neque cursus tellus odio adipiscing netus elit Donec. Vestibulum Cras ligula vitae pretium Curabitur eros Nam Lorem eros non. Sed id mauris justo tristique orci neque eleifend lacus lorem.' ) . "</p>\n";
     1566                $content .= '<p>' . __( 'Sed consequat Nullam et vel platea semper id mauris Nam eget. Sem neque a amet eu ipsum id dignissim neque eu pulvinar. Mauris nulla egestas et laoreet penatibus ipsum lobortis convallis congue libero. Tortor nibh pellentesque tellus odio Morbi cursus eros tincidunt tincidunt sociis. Egestas at In Donec mi dignissim Nam rutrum felis metus Maecenas. Sed tellus consectetuer.' ) . "</p>\n";
     1567                $content .= '<p>' . __( 'Justo orci pulvinar mauris tincidunt sed Pellentesque dis sapien tempor ligula. Dolor laoreet fames eros accumsan Integer feugiat nec augue Phasellus rutrum. Id Sed facilisi elit mus nulla at dapibus ut enim sociis. Fringilla ridiculus dui justo eu Maecenas ipsum ut aliquet magna non. Id magna adipiscing Vestibulum Curabitur vel pretium ac justo platea neque. Maecenas Donec Quisque urna interdum.' ) . "</p>\n";
     1568                $content .= '<p>' . __( 'Tellus sagittis leo adipiscing ante facilisis Aliquam tellus at at elit. Ut dignissim tempus eu Fusce Vestibulum at eros ante dis tempus. Sed libero orci at id ut pretium metus adipiscing justo malesuada. In tempus vitae commodo libero In neque sagittis turpis In In. Eleifend elit dis ac eros urna auctor semper quis odio pretium. Ut Aenean cursus.' ) . "</p>\n";
     1569
     1570                /**
     1571                 * Filters the default content suggested for inclusion in a privacy policy.
     1572                 *
     1573                 * @since 4.9.6
     1574                 *
     1575                 * @param $content string The defauld policy content.
     1576                 */
     1577                return apply_filters( 'wp_get_default_privcy_policy_content', $content );
     1578        }
     1579
     1580        /**
     1581         * Add the suggested privacy policy text to the policy postbox.
     1582         *
     1583         * @since 4.9.6
     1584         */
     1585        public static function add_suggested_content() {
     1586                $content = self::get_default_content();
     1587                wp_add_privacy_policy_content(  'WordPress', $content );
     1588        }
     1589}
  • branches/4.9/src/wp-admin/includes/plugin.php

    r42243 r43101  
    18971897        include( WP_PLUGIN_DIR . '/' . $plugin );
    18981898}
     1899
     1900/**
     1901 * Helper function for adding plugin specific information to the postbox shown when editing the privacy policy.
     1902 *
     1903 * Intended for use with the `'admin_init'` action.
     1904 *
     1905 * @since 4.9.6
     1906 *
     1907 * @param string $plugin_name The plugin'as name. Will be shown in the privacy policy metabox.
     1908 * @param string $policy_text The content that should appear in the site's privacy policy.
     1909 *                            For more information see the Plugins Handbook https://developer.wordpress.org/plugins/.
     1910 */
     1911function wp_add_privacy_policy_content( $plugin_name, $policy_text ) {
     1912        if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) {
     1913                require_once( ABSPATH . 'wp-admin/includes/misc.php' );
     1914        }
     1915
     1916        WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
     1917}
  • branches/4.9/src/wp-admin/js/post.js

    r38893 r43101  
    12651265                update();
    12661266        } );
     1267
     1268        // Privacy policy postbox, copy button.
     1269        $( document ).on( 'click', function( event ) {
     1270                var $target = $( event.target );
     1271                var node, range;
     1272
     1273                if ( $target.is( 'button.privacy-text-copy-button' ) ) {
     1274                        node = $target.parent().find( 'div.policy-text' )[0];
     1275
     1276                        if ( node ) {
     1277                                try {
     1278                                        window.getSelection().removeAllRanges();
     1279                                        range = document.createRange(); 
     1280                                        range.selectNode( node ); 
     1281                                        window.getSelection().addRange( range );
     1282
     1283                                        document.execCommand( 'copy' );
     1284                                        window.getSelection().removeAllRanges();
     1285                                } catch ( er ) {}
     1286                        }
     1287                }
     1288        });
     1289
    12671290} )( jQuery, new wp.utils.WordCounter() );
  • branches/4.9/src/wp-includes/default-filters.php

    r43095 r43101  
    533533add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 );
    534534
     535// Trigger the check for policy text changes after active plugins change.
     536add_action( 'update_site_option_active_sitewide_plugins', '_wp_privacy_active_plugins_change' );
     537add_action( 'update_option_active_plugins', '_wp_privacy_active_plugins_change' );
     538
    535539unset( $filter, $action );
  • branches/4.9/src/wp-includes/functions.php

    r43097 r43101  
    59375937
    59385938/**
     5939 * Trigger the check for policy text changes.
     5940 *
     5941 * @since 4.9.6
     5942 * @access private
     5943 */
     5944function _wp_privacy_active_plugins_change() {
     5945        update_option( '_wp_privacy_text_change_check', 'check' );
     5946}
     5947
     5948/**
    59395949 * Schedule a `WP_Cron` job to delete expired export files.
    59405950 *
  • branches/4.9/src/wp-includes/link-template.php

    r41589 r43101  
    41054105        return apply_filters( 'parent_theme_file_path', $path, $file );
    41064106}
     4107
     4108/**
     4109 * Retrieves the URL to the privacy policy page.
     4110 *
     4111 * @since 4.9.6
     4112 *
     4113 * @return string The URL to the privacy policy page. Empty string if it doesn't exist.
     4114 */
     4115function get_privacy_policy_url() {
     4116        $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     4117
     4118        if ( empty( $policy_page_id ) ) {
     4119                return '';
     4120        }
     4121
     4122        return get_permalink( $policy_page_id );
     4123}
Note: See TracChangeset for help on using the changeset viewer.