Make WordPress Core

Ticket #37013: 37013.6.diff

File 37013.6.diff, 379.2 KB (added by mihai2u, 7 years ago)
  • .travis.yml

    diff --git a/.travis.yml b/.travis.yml
    index 8196d39..e6c8ec7 100644
    a b cache: 
    44  apt: true
    55  directories:
    66    - node_modules
     7    - vendor
     8    - $HOME/.composer/cache
    79env:
    810  global:
    911    - WP_TRAVISCI=travis:phpunit
    1012matrix:
    1113  include:
    12   - php: 7.0
     14  - php: 7.1
    1315    env: WP_TRAVISCI=travis:js
    14   - php: 5.2
    15   - php: 5.3
    16   - php: 5.4
    17   - php: 5.5
     16  - php: 7.1
     17  - php: 7.0
    1818  - php: 5.6
    1919  - php: 5.6
    2020    env: WP_TRAVIS_OBJECT_CACHE=true
    2121    services: memcached
    22   - php: hhvm
    23     sudo: required
    24     dist: trusty
    25     group: edge
    26     addons:
    27       apt:
    28         packages:
    29         - mysql-server-5.6
    30         - mysql-client-core-5.6
    31         - mysql-client-5.6
    32   - php: 7.0
    33   - php: 7.1
     22  - php: 5.5
     23  - php: 5.4
     24  - php: 5.3
     25  - php: 5.2
    3426  - php: nightly
    3527  allow_failures:
    36   - php: hhvm
    3728  - php: nightly
    3829before_install:
    3930- |
    before_install: 
    4334      sed -i "s/youremptytestdbnamehere/wordpress_tests/" wp-tests-config.php
    4435      sed -i "s/yourusernamehere/root/" wp-tests-config.php
    4536      sed -i "s/yourpasswordhere//" wp-tests-config.php
    46       svn checkout https://plugins.svn.wordpress.org/wordpress-importer/trunk tests/phpunit/data/plugins/wordpress-importer
     37      svn checkout https://plugins.svn.wordpress.org/wordpress-importer/tags/0.6.3/ tests/phpunit/data/plugins/wordpress-importer
    4738  fi
    4839- |
    4940  if [[ "$WP_TRAVIS_OBJECT_CACHE" == "true" ]]; then
    50     curl https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/584392b56dc4adbe52bd2c7b86f875e23a3e5f75/object-cache.php > src/wp-content/object-cache.php
     41    cp tests/phpunit/includes/object-cache.php src/wp-content/object-cache.php
    5142    echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
    5243  fi
    5344before_script:
    5445- |
    55   # Remove Xdebug for a huge performance increase, but not from nightly or hhvm:
     46  # Remove Xdebug for a huge performance increase, but not from nightly:
    5647  stable='^[0-9\.]+$'
    5748  if [[ "$TRAVIS_PHP_VERSION" =~ $stable ]]; then
    5849    phpenv config-rm xdebug.ini
    before_script: 
    6758  # Install the specified version of PHPUnit depending on the PHP version:
    6859  if [[ "$WP_TRAVISCI" == "travis:phpunit" ]]; then
    6960    case "$TRAVIS_PHP_VERSION" in
    70       7.1|7.0|hhvm|nightly)
    71         echo "Using PHPUnit 5.7"
    72         composer global require "phpunit/phpunit=5.7.*"
     61      7.1|7.0|nightly)
     62        echo "Using PHPUnit 6.1"
     63        composer global require "phpunit/phpunit=6.1.*"
    7364        ;;
    7465      5.6|5.5|5.4|5.3)
    7566        echo "Using PHPUnit 4.8"
    before_script: 
    9485- mysql --version
    9586- phpenv versions
    9687- php --version
    97 - |
    98   # Debug PHP extensions, but not on HHVM because the command hangs indefinitely:
    99   if [[ "$TRAVIS_PHP_VERSION" != 'hhvm' ]]; then
    100     php -m
    101   fi
     88- php -m
    10289- npm --version
    10390- node --version
    10491- which phpunit
    before_script: 
    10794- grunt --version
    10895- git --version
    10996- svn --version
     97- locale -a
    11098script: grunt $WP_TRAVISCI
    11199notifications:
    112100  slack:
  • Gruntfile.js

    diff --git a/Gruntfile.js b/Gruntfile.js
    index 80e2968..14e558f 100644
    a b module.exports = function(grunt) { 
    426426                phpunit: {
    427427                        'default': {
    428428                                cmd: 'phpunit',
    429                                 args: ['-c', 'phpunit.xml.dist']
     429                                args: ['--verbose', '-c', 'phpunit.xml.dist']
    430430                        },
    431431                        ajax: {
    432432                                cmd: 'phpunit',
    433                                 args: ['-c', 'phpunit.xml.dist', '--group', 'ajax']
     433                                args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'ajax']
    434434                        },
    435435                        multisite: {
    436436                                cmd: 'phpunit',
    437                                 args: ['-c', 'tests/phpunit/multisite.xml']
     437                                args: ['--verbose', '-c', 'tests/phpunit/multisite.xml']
    438438                        },
    439439                        'external-http': {
    440440                                cmd: 'phpunit',
    441                                 args: ['-c', 'phpunit.xml.dist', '--group', 'external-http']
     441                                args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'external-http']
    442442                        },
    443443                        'restapi-jsclient': {
    444444                                cmd: 'phpunit',
    445                                 args: ['-c', 'phpunit.xml.dist', '--group', 'restapi-jsclient']
     445                                args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'restapi-jsclient']
    446446                        }
    447447                },
    448448                uglify: {
  • phpunit.xml.dist

    diff --git a/phpunit.xml.dist b/phpunit.xml.dist
    index 415ec76..0bb6491 100644
    a b  
    22        bootstrap="tests/phpunit/includes/bootstrap.php"
    33        backupGlobals="false"
    44        colors="true"
     5        beStrictAboutTestsThatDoNotTestAnything="true"
    56        >
    67    <testsuites>
    78        <!-- Default test suite to run all tests -->
     
    2324        <exclude>
    2425            <group>ajax</group>
    2526            <group>ms-files</group>
     27            <group>ms-required</group>
    2628            <group>external-http</group>
    2729        </exclude>
    2830    </groups>
  • src/wp-admin/css/color-picker.css

    diff --git a/src/wp-admin/css/color-picker.css b/src/wp-admin/css/color-picker.css
    index baa1d94..30dec29 100644
    a b  
    133133        box-shadow:
    134134                0 0 0 1px #5b9dd9,
    135135                0 0 2px 1px rgba(30, 140, 190, .8);
    136 }
    137  No newline at end of file
     136}
     137
     138@media screen and ( max-width: 782px ) {
     139        .wp-picker-container input[type="text"].wp-color-picker {
     140                margin-right: 6px;
     141                padding: 3px 5px;
     142        }
     143}
  • src/wp-admin/css/common.css

    diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css
    index d06bebe..9074743 100644
    a b hr { 
    865865        border-bottom: 1px solid #fafafa;
    866866}
    867867
    868 .widget-control-remove,
    869 .widget-control-remove:focus,
    870868.row-actions span.delete a,
    871869.row-actions span.trash a,
    872870.row-actions span.spam a,
    span.required, 
    893891#media-items a.delete:hover,
    894892#media-items a.delete-permanently:hover,
    895893#nav-menu-footer .menu-delete:hover {
    896         color: #f00;
     894        color: #dc3232;
    897895        border: none;
    898896}
    899897
    900 .widget-control-remove:hover {
    901         color: #f00;
    902 }
    903 
    904898/*------------------------------------------------------------------------------
    905899  3.0 - Actions
    906900------------------------------------------------------------------------------*/
    img { 
    30863080/* Metabox collapse arrow indicators */
    30873081.js .sidebar-name .sidebar-name-arrow:before,
    30883082.js .meta-box-sortables .postbox .toggle-indicator:before,
    3089 .bulk-action-notice .toggle-indicator:before {
    3090         content: "\f142";
     3083.bulk-action-notice .toggle-indicator:before,
     3084.js .accordion-section .handlediv .sidebar-name-arrow:before {
    30913085        display: inline-block;
    30923086        font: normal 20px/1 dashicons;
    30933087        speak: none;
    img { 
    30963090        text-decoration: none !important;
    30973091}
    30983092
     3093/* Open state */
     3094.js .sidebar-name .sidebar-name-arrow:before,
     3095.js .meta-box-sortables .postbox .toggle-indicator:before,
     3096.bulk-action-notice .toggle-indicator:before,
     3097.js .accordion-section.open .handlediv .sidebar-name-arrow:before {
     3098        content: "\f142";
     3099}
     3100
     3101/* Closed state */
    30993102.js .widgets-holder-wrap.closed .sidebar-name-arrow:before,
    31003103.js .meta-box-sortables .postbox.closed .handlediv .toggle-indicator:before,
    3101 .bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before {
     3104.bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before,
     3105.js .accordion-section .handlediv .sidebar-name-arrow:before {
    31023106        content: "\f140";
    31033107}
    31043108
    3105 .js .sidebar-name .sidebar-name-arrow:before {
    3106         padding: 10px;
    3107         left: 0;
    3108 }
    3109 
    31103109.js #widgets-left .sidebar-name .sidebar-name-arrow {
    31113110        display: none;
    31123111}
    img { 
    31493148                0 0 2px 1px rgba(30, 140, 190, .8);
    31503149}
    31513150
     3151.js .metabox-holder .accordion-section .accordion-section-title:after {
     3152        content: none;
     3153}
     3154
     3155.js .metabox-holder .accordion-section .handlediv {
     3156        float: right;
     3157        position: relative;
     3158        top: 11px;
     3159        margin-right: 11px;
     3160        z-index: 2;
     3161}
     3162
     3163.js .metabox-holder .accordion-section .handlediv .sidebar-name-arrow:before {
     3164        width: 20px;
     3165        border-radius: 50%;
     3166        text-indent: -1px; /* account for the dashicon alignment */
     3167}
     3168
     3169.js .metabox-holder .accordion-section .handlediv:focus {
     3170        outline: none;
     3171        border: none;
     3172        box-shadow: none;
     3173}
     3174
     3175.js .metabox-holder .accordion-section .handlediv:focus .sidebar-name-arrow:before {
     3176        box-shadow:
     3177                0 0 0 1px #5b9dd9,
     3178                0 0 2px 1px rgba(30, 140, 190, .8);
     3179}
     3180
    31523181/* @todo: appears to be Press This only and overridden */
    31533182#photo-add-url-div input[type="text"] {
    31543183        width: 300px;
    img { 
    32443273
    32453274/* @todo: can we use a common class for these? */
    32463275.nav-menus-php .item-edit:before,
    3247 .widget-top a.widget-action:after,
     3276.widget-top .widget-action .toggle-indicator:before,
    32483277.control-section .accordion-section-title:after,
    3249 .accordion-section-title:after {
     3278.accordion-section-title:after,
     3279.accordion-section-arrow:after {
    32503280        right: 0;
    32513281        content: "\f140";
    3252         border: none;
    3253         background: none;
    32543282        font: normal 20px/1 dashicons;
    32553283        speak: none;
    32563284        display: block;
    3257         padding: 0;
    3258         text-indent: 0;
    3259         text-align: center;
    3260         position: relative;
    32613285        -webkit-font-smoothing: antialiased;
    32623286        -moz-osx-font-smoothing: grayscale;
    32633287        text-decoration: none !important;
    32643288}
    32653289
     3290.widget-top .widget-action .toggle-indicator:before {
     3291        padding: 1px 2px 1px 0px;
     3292        -webkit-border-radius: 50%;
     3293        border-radius: 50%;
     3294}
     3295
    32663296.handlediv,
    32673297.postbox .handlediv.button-link,
    32683298.item-edit,
    img { 
    32883318        color: #23282d;
    32893319}
    32903320
    3291 .widget-top a.widget-action:after {
    3292         padding: 1px 2px 1px 0px;
    3293         margin-top: 10px;
    3294         margin-right: 10px;
    3295         -webkit-border-radius: 50%;
    3296         border-radius: 50%;
    3297 }
    3298 
    3299 .widget-top a.widget-action:focus:after {
     3321.widget-top .widget-action:focus .toggle-indicator:before {
    33003322        -webkit-box-shadow:
    33013323                0 0 0 1px #5b9dd9,
    33023324                0 0 2px 1px rgba(30,140,190,.8);
    img { 
    33153337.control-section.open .accordion-section-title:after,
    33163338#customize-info.open .accordion-section-title:after,
    33173339.nav-menus-php .menu-item-edit-active .item-edit:before,
    3318 .widget.open .widget-top a.widget-action:after {
     3340.widget.open .widget-top .widget-action .toggle-indicator:before,
     3341.open .accordion-section-arrow:after {
    33193342        content: "\f142";
    33203343}
    33213344
    img { 
    35943617        /* @todo: evaluate - most of these were likely replaced by dashicons */
    35953618        .curtime #timestamp,
    35963619        #screen-meta-links a.show-settings,
    3597         .widget-top a.widget-action,
    3598         .widget-top a.widget-action:hover,
     3620        .widget-top .widget-action,
     3621        .widget-top .widget-action:hover,
    35993622        .sidebar-name-arrow,
    36003623        .sidebar-name:hover .sidebar-name-arrow,
    36013624        .meta-box-sortables .postbox:hover .handlediv,
  • src/wp-admin/css/customize-controls.css

    diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css
    index fbcfe64..d033b89 100644
    a b p.customize-section-description { 
    617617
    618618.customize-control select {
    619619        width: 100%;
    620         max-width: 300px;
    621620        height: 28px;
    622621        line-height: 28px;
    623622}
    p.customize-section-description { 
    744743
    745744.customize-control-dropdown-pages .new-content-item {
    746745        width: 100%;
    747         max-width: 300px;
    748746        padding: 5px 0 5px 1px;
    749747        position: relative;
    750748}
    p.customize-section-description { 
    11891187.customize-section-description-container + #customize-control-custom_css:last-child {
    11901188        margin-left: -12px;
    11911189        width: 299px;
     1190        width: -webkit-calc( 100% + 24px );
     1191        width: calc( 100% + 24px );
    11921192        margin-bottom: -12px;
    11931193}
    11941194
    11951195@media screen and ( max-width: 640px ) {
    11961196        .customize-section-description-container + #customize-control-custom_css:last-child {
    1197                 margin-left: 0;
    11981197                margin-right: 0;
    1199                 width: 100%;
    12001198        }
    12011199        .customize-section-description-container + #customize-control-custom_css:last-child textarea {
    12021200                height: -webkit-calc( 100vh - 140px );
    12031201                height: calc( 100vh - 140px );
    1204                 width: 100%;
    1205                 border: solid 1px #ddd;
    12061202        }
    12071203}
    12081204
  • src/wp-admin/css/customize-nav-menus.css

    diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css
    index c42fa54..f2186fb 100644
    a b  
    153153        text-align: center;
    154154}
    155155
    156 .wp-customizer .menu-item.menu-item-edit-active .item-edit .toggle-indicator:after {
     156.wp-customizer .menu-item.menu-item-edit-active .item-edit .toggle-indicator:before {
    157157        content: "\f142";
    158158}
    159159
     
    271271        display: inline-block;
    272272        font-size: 20px;
    273273        line-height: 1;
    274         text-indent: -1px; /* account for the dashicon alignment */
    275274}
    276275
    277276.rtl .wp-customizer .toggle-indicator {
    278277        text-indent: 1px; /* account for the dashicon alignment */
    279278}
    280279
    281 .wp-customizer .toggle-indicator:after {
     280.wp-customizer .menu-item .item-edit .toggle-indicator:before,
     281#available-menu-items .accordion-section-title .toggle-indicator:before {
    282282        content: "\f140";
     283        display: block;
     284        padding: 1px 2px 1px 0px;
    283285        speak: none;
    284         vertical-align: top;
    285286        -webkit-border-radius: 50%;
    286287        border-radius: 50%;
    287288        color: #72777c;
     
    494495        content: none !important;
    495496}
    496497
    497 #available-menu-items .accordion-section-title:hover .toggle-indicator:after,
    498 #available-menu-items .button-link:hover .toggle-indicator:after,
    499 #available-menu-items .button-link:focus .toggle-indicator:after {
     498#available-menu-items .accordion-section-title:hover .toggle-indicator:before,
     499#available-menu-items .button-link:hover .toggle-indicator:before,
     500#available-menu-items .button-link:focus .toggle-indicator:before {
    500501        color: #23282d;
    501502}
    502503
    503 #available-menu-items .open .accordion-section-title .toggle-indicator:after {
     504#available-menu-items .open .accordion-section-title .toggle-indicator:before {
    504505        content: "\f142";
    505506        color: #23282d;
    506507}
    li.assigned-to-menu-location .add-new-menu-item { 
    837838.wp-customizer .menu-item .submitbox .submitdelete:focus,
    838839.customize-screen-options-toggle:focus:before,
    839840#customize-controls .customize-info .customize-help-toggle:focus:before,
    840 .wp-customizer button:focus .toggle-indicator:after,
     841.wp-customizer button:focus .toggle-indicator:before,
    841842.menu-delete:focus,
    842843.menu-item-bar .item-delete:focus:before,
    843844#available-menu-items .item-add:focus:before {
  • src/wp-admin/css/customize-widgets.css

    diff --git a/src/wp-admin/css/customize-widgets.css b/src/wp-admin/css/customize-widgets.css
    index 687c9f8..428c455 100644
    a b  
    8989        line-height: 16px;
    9090}
    9191
    92 .customize-control-widget_form.expanded a.widget-action:after {
     92.customize-control-widget_form.expanded .widget-action .toggle-indicator:before {
    9393        content: "\f142";
    9494}
    9595
    96 .customize-control-widget_form.wide-widget-control a.widget-action:after {
     96.customize-control-widget_form.wide-widget-control .widget-action .toggle-indicator:before {
    9797        content: "\f139";
    9898}
    9999
    100 .customize-control-widget_form.wide-widget-control.expanded a.widget-action:after {
     100.customize-control-widget_form.wide-widget-control.expanded .widget-action .toggle-indicator:before {
    101101        content: "\f141";
    102102}
    103103
    body.adding-widget #customize-preview { 
    438438        .last-widget {
    439439                margin-bottom: 15px;
    440440        }
     441        /* This rule reduces the widgets titles height. */
    441442        .widget-title h3 {
    442443                padding: 13px 15px;
    443444        }
     445        .widget-top .widget-action {
     446                padding-bottom: 8px;
     447        }
    444448        .widget-reorder-nav span {
    445449                height: 39px;
    446450        }
  • src/wp-admin/css/dashboard.css

    diff --git a/src/wp-admin/css/dashboard.css b/src/wp-admin/css/dashboard.css
    index d467f0c..98fb993 100644
    a b  
    308308        padding: 0;
    309309}
    310310
    311 #dashboard_primary .widget-loading,
    312 #dashboard_primary .dashboard-widget-control-form {
     311#dashboard_primary .widget-loading {
    313312        padding: 12px 12px 0;
     313        margin-bottom: 1em !important; /* Needs to override `.postbox .inside > p:last-child` in common.css */
    314314}
    315315
    316 body #dashboard-widgets .postbox form .submit {
     316/* Notice when JS is off. */
     317#dashboard_primary .inside .notice {
    317318        margin: 0;
    318319}
    319320
    320 .dashboard-widget-control-form {
    321         overflow: hidden;
     321body #dashboard-widgets .postbox form .submit {
     322        margin: 0;
    322323}
    323324
     325/* Used only for configurable widgets. */
    324326.dashboard-widget-control-form p {
    325327        margin-top: 0;
    326328}
  • src/wp-admin/css/themes.css

    diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css
    index a18a89a..86fc133 100644
    a b body.full-overlay-active { 
    13321332        -moz-box-sizing: border-box;
    13331333        box-sizing: border-box;
    13341334        position: fixed;
    1335         width: 300px;
     1335        min-width: 300px;
     1336        max-width: 600px;
     1337        width: 18%;
    13361338        height: 100%;
    13371339        top: 0;
    13381340        bottom: 0;
    body.full-overlay-active { 
    13611363        margin-left: -300px;
    13621364}
    13631365
     1366@media screen and (min-width: 1667px) {
     1367        .wp-full-overlay.expanded {
     1368                margin-left: 18%;
     1369        }
     1370
     1371        .wp-full-overlay.collapsed .wp-full-overlay-sidebar {
     1372                margin-left: -18%;
     1373        }
     1374}
     1375
     1376@media screen and (min-width: 3333px) {
     1377        .wp-full-overlay.expanded {
     1378                margin-left: 600px;
     1379        }
     1380
     1381        .wp-full-overlay.collapsed .wp-full-overlay-sidebar {
     1382                margin-left: -600px;
     1383        }
     1384}
     1385
    13641386.wp-full-overlay-sidebar:after {
    13651387        content: "";
    13661388        display: block;
    body.full-overlay-active { 
    15201542        box-shadow: none !important;
    15211543        -webkit-border-radius: 0 !important;
    15221544        border-radius: 0 !important;
    1523         z-index: -1; /* Below device buttons */
    15241545}
    15251546
    15261547.wp-core-ui .wp-full-overlay .collapse-sidebar:hover,
    body.full-overlay-active { 
    16161637        position: fixed;
    16171638        bottom: 0;
    16181639        left: 0;
    1619         width: 299px;
     1640        min-width: 299px;
     1641        max-width: 599px;
     1642        width: 18%;
    16201643        height: 45px;
    16211644        border-top: 1px solid #ddd;
    16221645        background: #eee;
    16231646}
    16241647
    1625 .wp-full-overlay-footer .devices {
     1648.wp-full-overlay-footer .devices-wrapper {
    16261649        float: right;
     1650}
     1651
     1652.wp-full-overlay-footer .devices {
     1653        position: relative;
    16271654        background: #eee;
    16281655        -webkit-box-shadow: -20px 0 10px -5px #eee;
    16291656        box-shadow: -20px 0 10px -5px #eee;
  • src/wp-admin/css/widgets.css

    diff --git a/src/wp-admin/css/widgets.css b/src/wp-admin/css/widgets.css
    index f0be076..bc794e5 100644
    a b  
    1414        background: #f7f7f7;
    1515}
    1616
    17 .widget-top a.widget-action,
    18 .widget-top a.widget-action:hover {
    19         -webkit-box-shadow: none;
    20         box-shadow: none;
     17.widget-top .widget-action {
     18        border: 0;
     19        margin: 0;
     20        padding: 10px;
     21        background: none;
     22        cursor: pointer;
    2123        outline: none;
    22         text-decoration: none;
    2324}
    2425
    2526.widget-title h3,
     
    5051}
    5152
    5253.deleting .widget-title,
    53 .deleting .widget-top a.widget-action:after {
     54.deleting .widget-top .widget-action .toggle-indicator:before {
    5455        color: #a0a5aa;
    5556}
    5657
     
    9192        box-sizing: border-box;
    9293}
    9394
    94 .sidebar-name-arrow {
    95         position: absolute;
    96         top: 0;
    97         right: 0;
    98         bottom: 0;
     95.js .sidebar-name .handlediv {
     96        float: right;
     97    width: 32px;
     98    height: 32px;
     99    padding: 0;
     100}
     101
     102.js .sidebar-name .handlediv:focus {
     103        box-shadow: none;
     104        outline: none;
     105}
     106
     107.js #widgets-left .sidebar-name .handlediv:focus .sidebar-name-arrow {
     108        display: block;
     109}
     110
     111.js .sidebar-name .handlediv:focus .sidebar-name-arrow:before {
     112        box-shadow:
     113                0 0 0 1px #5b9dd9,
     114                0 0 2px 1px rgba(30, 140, 190, .8);
     115}
     116
     117.js .sidebar-name .sidebar-name-arrow:before {
     118        margin-top: 14px;
     119        width: 20px;
     120        border-radius: 50%;
     121        text-indent: -1px; /* account for the dashicon alignment */
     122}
     123
     124.js .widget-liquid-left .sidebar-name .sidebar-name-arrow:before {
     125        margin-top: 7px;
    99126}
    100127
    101128.js .sidebar-name {
    div#widgets-left .sidebar-name h3 { 
    156183        margin: 0 10px 0 0;
    157184}
    158185
    159 #widgets-left .sidebar-name .sidebar-name-arrow:before {
    160         padding: 9px;
    161 }
    162 
    163186#widgets-left .widgets-holder-wrap,
    164187div#widgets-left .widget-holder {
    165188        background: transparent;
    div#widgets-right .sidebar-name h3 { 
    249272        padding: 15px 7px;
    250273}
    251274
    252 div#widgets-right .sidebar-name .sidebar-name-arrow:before {
    253         top: 2px;
    254 }
    255 
    256275div#widgets-right .widget-top {
    257276        padding: 0;
    258277}
  • src/wp-admin/customize.php

    diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php
    index f1e66ff..b6c1e63 100644
    a b  
    171171                </div>
    172172
    173173                <div id="customize-footer-actions" class="wp-full-overlay-footer">
    174                         <?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
    175                         <?php if ( ! empty( $previewable_devices ) ) : ?>
    176                         <div class="devices">
    177                                 <?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
    178                                         <?php
    179                                         if ( empty( $settings['label'] ) ) {
    180                                                 continue;
    181                                         }
    182                                         $active = ! empty( $settings['default'] );
    183                                         $class = 'preview-' . $device;
    184                                         if ( $active ) {
    185                                                 $class .= ' active';
    186                                         }
    187                                         ?>
    188                                         <button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
    189                                                 <span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
    190                                         </button>
    191                                 <?php endforeach; ?>
    192                         </div>
    193                         <?php endif; ?>
    194174                        <button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php echo esc_attr( _x( 'Hide Controls', 'label for hide controls button without length constraints' ) ); ?>">
    195175                                <span class="collapse-sidebar-arrow"></span>
    196176                                <span class="collapse-sidebar-label"><?php _ex( 'Hide Controls', 'short (~12 characters) label for hide controls button' ); ?></span>
    197177                        </button>
     178                        <?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
     179                        <?php if ( ! empty( $previewable_devices ) ) : ?>
     180                        <div class="devices-wrapper">
     181                                <div class="devices">
     182                                        <?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
     183                                                <?php
     184                                                if ( empty( $settings['label'] ) ) {
     185                                                        continue;
     186                                                }
     187                                                $active = ! empty( $settings['default'] );
     188                                                $class = 'preview-' . $device;
     189                                                if ( $active ) {
     190                                                        $class .= ' active';
     191                                                }
     192                                                ?>
     193                                                <button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
     194                                                        <span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
     195                                                </button>
     196                                        <?php endforeach; ?>
     197                                </div>
     198                        </div>
     199                        <?php endif; ?>
    198200                </div>
    199201        </form>
    200202        <div id="customize-preview" class="wp-full-overlay-main"></div>
  • src/wp-admin/includes/ajax-actions.php

    diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php
    index 90a9dd1..3342cad 100644
    a b function wp_ajax_wp_link_ajax() { 
    14971497
    14981498        $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
    14991499
    1500         require(ABSPATH . WPINC . '/class-wp-editor.php');
     1500        if ( ! class_exists( '_WP_Editors', false ) ) {
     1501                require( ABSPATH . WPINC . '/class-wp-editor.php' );
     1502        }
     1503
    15011504        $results = _WP_Editors::wp_link_query( $args );
    15021505
    15031506        if ( ! isset( $results ) )
  • src/wp-admin/includes/dashboard.php

    diff --git a/src/wp-admin/includes/dashboard.php b/src/wp-admin/includes/dashboard.php
    index b763d43..be0b201 100644
    a b function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_ 
    191191 * @param array $meta_box
    192192 */
    193193function _wp_dashboard_control_callback( $dashboard, $meta_box ) {
    194         echo '<form method="post" class="dashboard-widget-control-form">';
     194        echo '<form method="post" class="dashboard-widget-control-form wp-clearfix">';
    195195        wp_dashboard_trigger_widget_control( $meta_box['id'] );
    196196        wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' );
    197197        echo '<input type="hidden" name="widget_id" value="' . esc_attr($meta_box['id']) . '" />';
    function wp_dashboard_rss_output( $widget_id ) { 
    968968 * @return bool False on failure. True on success.
    969969 */
    970970function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array() ) {
    971         $loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading&#8230;' ) . '</p><p class="hide-if-js">' . __( 'This widget requires JavaScript.' ) . '</p>';
     971        $loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading&#8230;' ) . '</p><div class="hide-if-js notice notice-error inline"><p>' . __( 'This widget requires JavaScript.' ) . '</p></div>';
    972972        $doing_ajax = wp_doing_ajax();
    973973
    974974        if ( empty($check_urls) ) {
  • src/wp-admin/includes/meta-boxes.php

    diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php
    index 23ca247..ba43c7d 100644
    a b function post_submit_meta_box( $post, $args = array() ) { 
    5050<?php
    5151$preview_link = esc_url( get_preview_post_link( $post ) );
    5252if ( 'publish' == $post->post_status ) {
    53         $preview_button = __( 'Preview Changes' );
     53        $preview_button_text = __( 'Preview Changes' );
    5454} else {
    55         $preview_button = __( 'Preview' );
     55        $preview_button_text = __( 'Preview' );
    5656}
     57
     58$preview_button = sprintf( '%1$s<span class="screen-reader-text"> %2$s</span>',
     59        $preview_button_text,
     60        /* translators: accessibility text */
     61        __( '(link opens in a new window)' )
     62);
    5763?>
    5864<a class="preview button" href="<?php echo $preview_link; ?>" target="wp-preview-<?php echo (int) $post->ID; ?>" id="post-preview"><?php echo $preview_button; ?></a>
    5965<input type="hidden" name="wp-preview" id="wp-preview" value="" />
  • src/wp-admin/includes/ms.php

    diff --git a/src/wp-admin/includes/ms.php b/src/wp-admin/includes/ms.php
    index 6a4ca9d..939d0d0 100644
    a b function mu_dropdown_languages( $lang_files = array(), $current = '' ) { 
    785785function site_admin_notice() {
    786786        global $wp_db_version, $pagenow;
    787787
    788         if ( ! is_super_admin() ) {
     788        if ( ! current_user_can( 'upgrade_network' ) ) {
    789789                return false;
    790790        }
    791791
  • src/wp-admin/includes/options.php

    diff --git a/src/wp-admin/includes/options.php b/src/wp-admin/includes/options.php
    index 95d9964..14c175d 100644
    a b function options_general_add_js() { 
    5353                        if ( "date_format_custom_radio" != $(this).attr("id") )
    5454                                $( "input[name='date_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
    5555                });
    56                 $("input[name='date_format_custom']").focus(function(){
     56                $( 'input[name="date_format_custom"]' ).on( 'click input', function() {
    5757                        $( '#date_format_custom_radio' ).prop( 'checked', true );
    5858                });
    5959
    function options_general_add_js() { 
    6161                        if ( "time_format_custom_radio" != $(this).attr("id") )
    6262                                $( "input[name='time_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
    6363                });
    64                 $("input[name='time_format_custom']").focus(function(){
     64                $( 'input[name="time_format_custom"]' ).on( 'click input', function() {
    6565                        $( '#time_format_custom_radio' ).prop( 'checked', true );
    6666                });
    6767                $("input[name='date_format_custom'], input[name='time_format_custom']").change( function() {
  • src/wp-admin/includes/post.php

    diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php
    index 1054f2b..8055a7b 100644
    a b function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) { 
    14481448 *
    14491449 * @since 2.5.0
    14501450 *
    1451  * @param int $post_id ID of the post to check for editing
    1452  * @return integer False: not locked or locked by current user. Int: user ID of user with lock.
     1451 * @param int $post_id ID of the post to check for editing.
     1452 * @return int|false ID of the user with lock. False if the post does not exist, post is not locked,
     1453 *                   the user with lock does not exist, or the post is locked by current user.
    14531454 */
    14541455function wp_check_post_lock( $post_id ) {
    1455         if ( !$post = get_post( $post_id ) )
     1456        if ( ! $post = get_post( $post_id ) ) {
    14561457                return false;
     1458        }
    14571459
    1458         if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) )
     1460        if ( ! $lock = get_post_meta( $post->ID, '_edit_lock', true ) ) {
    14591461                return false;
     1462        }
    14601463
    14611464        $lock = explode( ':', $lock );
    14621465        $time = $lock[0];
    14631466        $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
    14641467
     1468        if ( ! get_userdata( $user ) ) {
     1469                return false;
     1470        }
     1471
    14651472        /** This filter is documented in wp-admin/includes/ajax-actions.php */
    14661473        $time_window = apply_filters( 'wp_check_post_lock_window', 150 );
    14671474
    1468         if ( $time && $time > time() - $time_window && $user != get_current_user_id() )
     1475        if ( $time && $time > time() - $time_window && $user != get_current_user_id() ) {
    14691476                return $user;
     1477        }
     1478
    14701479        return false;
    14711480}
    14721481
    function wp_check_post_lock( $post_id ) { 
    14751484 *
    14761485 * @since 2.5.0
    14771486 *
    1478  * @param int $post_id ID of the post to being edited
    1479  * @return bool|array Returns false if the post doesn't exist of there is no current user, or
    1480  *      an array of the lock time and the user ID.
     1487 * @param int $post_id ID of the post being edited.
     1488 * @return array|false Array of the lock time and user ID. False if the post does not exist, or
     1489 *                     there is no current user.
    14811490 */
    14821491function wp_set_post_lock( $post_id ) {
    1483         if ( !$post = get_post( $post_id ) )
     1492        if ( ! $post = get_post( $post_id ) ) {
    14841493                return false;
    1485         if ( 0 == ($user_id = get_current_user_id()) )
     1494        }
     1495
     1496        if ( 0 == ( $user_id = get_current_user_id() ) ) {
    14861497                return false;
     1498        }
    14871499
    14881500        $now = time();
    14891501        $lock = "$now:$user_id";
    14901502
    14911503        update_post_meta( $post->ID, '_edit_lock', $lock );
     1504
    14921505        return array( $now, $user_id );
    14931506}
    14941507
  • src/wp-admin/includes/schema.php

    diff --git a/src/wp-admin/includes/schema.php b/src/wp-admin/includes/schema.php
    index 70b4686..64ec6b7 100644
    a b function populate_network( $network_id = 1, $domain = '', $email = '', $site_nam 
    951951
    952952        if ( !is_multisite() ) {
    953953                $site_admins = array( $site_user->user_login );
    954                 $users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) );
     954                $users = get_users( array(
     955                        'fields' => array( 'user_login' ),
     956                        'role'   => 'administrator',
     957                ) );
    955958                if ( $users ) {
    956959                        foreach ( $users as $user ) {
    957                                 if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) )
    958                                         $site_admins[] = $user->user_login;
     960                                $site_admins[] = $user->user_login;
    959961                        }
     962
     963                        $site_admins = array_unique( $site_admins );
    960964                }
    961965        } else {
    962966                $site_admins = get_site_option( 'site_admins' );
  • src/wp-admin/includes/template.php

    diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php
    index a4672fd..f568e39 100644
    a b function do_accordion_sections( $screen, $context, $object ) { 
    11611161                                        }
    11621162                                        ?>
    11631163                                        <li class="control-section accordion-section <?php echo $hidden_class; ?> <?php echo $open_class; ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>">
    1164                                                 <h3 class="accordion-section-title hndle" tabindex="0">
     1164                                                <button type="button" class="handlediv button-link" aria-expanded="true">
     1165                                                        <span class="screen-reader-text"><?php echo sprintf( __( 'Toggle panel: %s' ), esc_html( $box['title'] ) ); ?></span>
     1166                                                        <span class="sidebar-name-arrow" aria-hidden="true"></span>
     1167                                                </button>
     1168                                                <h3 class="accordion-section-title hndle">
    11651169                                                        <?php echo esc_html( $box['title'] ); ?>
    1166                                                         <span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
    11671170                                                </h3>
    11681171                                                <div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
    11691172                                                        <div class="inside">
  • src/wp-admin/includes/widgets.php

    diff --git a/src/wp-admin/includes/widgets.php b/src/wp-admin/includes/widgets.php
    index fe2a957..408165c 100644
    a b function wp_list_widget_controls( $sidebar, $sidebar_name = '' ) { 
    8080        if ( $sidebar_name ) {
    8181                ?>
    8282                <div class="sidebar-name">
    83                         <div class="sidebar-name-arrow"><br /></div>
     83                        <button type="button" class="handlediv button-link" aria-expanded="true">
     84                                <span class="screen-reader-text"><?php echo sprintf( __( 'Toggle panel: %s' ), $sidebar_name ); ?></span>
     85                                <span class="sidebar-name-arrow" aria-hidden="true"></span>
     86                        </button>
    8487                        <h2><?php echo esc_html( $sidebar_name ); ?> <span class="spinner"></span></h2>
    8588                </div>
    8689                <?php
    function wp_widget_control( $sidebar_args ) { 
    219222        echo $sidebar_args['before_widget']; ?>
    220223        <div class="widget-top">
    221224        <div class="widget-title-action">
    222                 <a class="widget-action hide-if-no-js" href="#available-widgets"></a>
     225                <button type="button" class="widget-action hide-if-no-js" aria-expanded="false">
     226                        <span class="screen-reader-text"><?php printf( __( 'Edit widget: %s' ), $widget_title ); ?></span>
     227                        <span class="toggle-indicator" aria-hidden="true"></span>
     228                </button>
    223229                <a class="widget-control-edit hide-if-js" href="<?php echo esc_url( add_query_arg( $query_arg ) ); ?>">
    224230                        <span class="edit"><?php _ex( 'Edit', 'widget' ); ?></span>
    225231                        <span class="add"><?php _ex( 'Add', 'widget' ); ?></span>
    function wp_widget_control( $sidebar_args ) { 
    250256
    251257        <div class="widget-control-actions">
    252258                <div class="alignleft">
    253                 <a class="widget-control-remove" href="#remove"><?php _e('Delete'); ?></a> |
    254                 <a class="widget-control-close" href="#close"><?php _e('Close'); ?></a>
     259                        <button type="button" class="button-link button-link-delete widget-control-remove"><?php _e( 'Delete' ); ?></button> |
     260                        <button type="button" class="button-link widget-control-close"><?php _e( 'Close' ); ?></button>
    255261                </div>
    256262                <div class="alignright<?php if ( 'noform' === $has_form ) echo ' widget-control-noform'; ?>">
    257263                        <?php submit_button( __( 'Save' ), 'primary widget-control-save right', 'savewidget', false, array( 'id' => 'widget-' . esc_attr( $id_format ) . '-savewidget' ) ); ?>
  • src/wp-admin/js/accordion.js

    diff --git a/src/wp-admin/js/accordion.js b/src/wp-admin/js/accordion.js
    index af62815..6eb919a 100644
    a b  
    3333        $( document ).ready( function () {
    3434
    3535                // Expand/Collapse accordion sections on click.
    36                 $( '.accordion-container' ).on( 'click keydown', '.accordion-section-title', function( e ) {
     36                $( '.accordion-container' ).on( 'click keydown', '.accordion-section-title, .handlediv', function( e ) {
    3737                        if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key
    3838                                return;
    3939                        }
  • src/wp-admin/js/customize-controls.js

    diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js
    index e038dcc..d340a1b 100644
    a b  
    47624762                         */
    47634763                        populateChangesetUuidParam = function( isIncluded ) {
    47644764                                var urlParser, queryParams;
     4765
     4766                                // Abort on IE9 which doesn't support history management.
     4767                                if ( ! history.replaceState ) {
     4768                                        return;
     4769                                }
     4770
    47654771                                urlParser = document.createElement( 'a' );
    47664772                                urlParser.href = location.href;
    47674773                                queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
     
    47804786                                history.replaceState( {}, document.title, urlParser.href );
    47814787                        };
    47824788
    4783                         if ( history.replaceState ) {
    4784                                 changesetStatus.bind( function( newStatus ) {
    4785                                         populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );
    4786                                 } );
    4787                         }
     4789                        changesetStatus.bind( function( newStatus ) {
     4790                                populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );
     4791                        } );
    47884792
    47894793                        // Expose states to the API.
    47904794                        api.state = state;
  • src/wp-admin/js/customize-widgets.js

    diff --git a/src/wp-admin/js/customize-widgets.js b/src/wp-admin/js/customize-widgets.js
    index b482e8d..df4c8d7 100644
    a b  
    952952                        var self = this, $removeBtn, replaceDeleteWithRemove;
    953953
    954954                        // Configure remove button
    955                         $removeBtn = this.container.find( 'a.widget-control-remove' );
     955                        $removeBtn = this.container.find( '.widget-control-remove' );
    956956                        $removeBtn.on( 'click', function( e ) {
    957957                                e.preventDefault();
    958958
     
    988988                        } );
    989989
    990990                        replaceDeleteWithRemove = function() {
    991                                 $removeBtn.text( l10n.removeBtnLabel ); // wp_widget_control() outputs the link as "Delete"
     991                                $removeBtn.text( l10n.removeBtnLabel ); // wp_widget_control() outputs the button as "Delete"
    992992                                $removeBtn.attr( 'title', l10n.removeBtnTooltip );
    993993                        };
    994994
     
    13671367                 * @param {Object} args  merged on top of this.defaultActiveArguments
    13681368                 */
    13691369                onChangeExpanded: function ( expanded, args ) {
    1370                         var self = this, $widget, $inside, complete, prevComplete, expandControl;
     1370                        var self = this, $widget, $inside, complete, prevComplete, expandControl, $toggleBtn;
    13711371
    13721372                        self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
    13731373                        if ( expanded ) {
     
    13861386
    13871387                        $widget = this.container.find( 'div.widget:first' );
    13881388                        $inside = $widget.find( '.widget-inside:first' );
     1389                        $toggleBtn = this.container.find( '.widget-top button.widget-action' );
    13891390
    13901391                        expandControl = function() {
    13911392
     
    13991400                                complete = function() {
    14001401                                        self.container.removeClass( 'expanding' );
    14011402                                        self.container.addClass( 'expanded' );
     1403                                        $toggleBtn.attr( 'aria-expanded', 'true' );
    14021404                                        self.container.trigger( 'expanded' );
    14031405                                };
    14041406                                if ( args.completeCallback ) {
     
    14281430                                        expandControl();
    14291431                                }
    14301432                        } else {
    1431 
    14321433                                complete = function() {
    14331434                                        self.container.removeClass( 'collapsing' );
    14341435                                        self.container.removeClass( 'expanded' );
     1436                                        $toggleBtn.attr( 'aria-expanded', 'false' );
    14351437                                        self.container.trigger( 'collapsed' );
    14361438                                };
    14371439                                if ( args.completeCallback ) {
  • src/wp-admin/js/editor.js

    diff --git a/src/wp-admin/js/editor.js b/src/wp-admin/js/editor.js
    index a63a51c..917721a 100644
    a b  
     1window.wp = window.wp || {};
     2
     3( function( $, wp ) {
     4        wp.editor = wp.editor || {};
    15
    2 ( function( $ ) {
    36        /**
    47         * @summary Utility functions for the editor.
    58         *
     
    484487                        } );
    485488                }
    486489
    487                 window.wp = window.wp || {};
    488                 window.wp.editor = window.wp.editor || {};
    489                 window.wp.editor.autop = wpautop;
    490                 window.wp.editor.removep = pre_wpautop;
     490                wp.editor.autop = wpautop;
     491                wp.editor.removep = pre_wpautop;
    491492
    492493                exports = {
    493494                        go: switchEditor,
     
    505506         * Expose the switch editors to be used globally.
    506507         */
    507508        window.switchEditors = new SwitchEditors();
    508 }( window.jQuery ));
     509
     510        /**
     511         * Initialize TinyMCE and/or Quicktags. For use with wp_enqueue_editor() (PHP).
     512         *
     513         * Intended for use with an existing textarea that will become the Text editor tab.
     514         * The editor width will be the width of the textarea container, height will be adjustable.
     515         *
     516         * Settings for both TinyMCE and Quicktags can be passed on initialization, and are "filtered"
     517         * with custom jQuery events on the document element, wp-before-tinymce-init and wp-before-quicktags-init.
     518         *
     519         * @since 4.8
     520         *
     521         * @param {string} id The HTML id of the textarea that is used for the editor.
     522         *                    Has to be jQuery compliant. No brackets, special chars, etc.
     523         * @param {object} settings Example:
     524         * settings = {
     525         *    // See https://www.tinymce.com/docs/configure/integration-and-setup/.
     526         *    // Alternatively set to `true` to use the defaults.
     527         *    tinymce: {
     528         *        setup: function( editor ) {
     529         *            console.log( 'Editor initialized', editor );
     530         *        }
     531         *    }
     532         *
     533         *    // Alternatively set to `true` to use the defaults.
     534         *        quicktags: {
     535         *        buttons: 'strong,em,link'
     536         *    }
     537         * }
     538         */
     539        wp.editor.initialize = function( id, settings ) {
     540                var init;
     541                var defaults;
     542
     543                if ( ! $ || ! id || ! wp.editor.getDefaultSettings ) {
     544                        return;
     545                }
     546
     547                defaults = wp.editor.getDefaultSettings();
     548
     549                // Initialize TinyMCE by default
     550                if ( ! settings ) {
     551                        settings = {
     552                                tinymce: true
     553                        };
     554                }
     555
     556                // Add wrap and the Visual|Text tabs.
     557                if ( settings.tinymce && settings.quicktags ) {
     558                        var $textarea = $( '#' + id );
     559                        var $wrap = $( '<div>' ).attr( {
     560                                        'class': 'wp-core-ui wp-editor-wrap tmce-active',
     561                                        id: 'wp-' + id + '-wrap'
     562                                } );
     563                        var $editorContainer = $( '<div class="wp-editor-container">' );
     564                        var $button = $( '<button>' ).attr( {
     565                                        type: 'button',
     566                                        'data-wp-editor-id': id
     567                                } );
     568
     569                        $wrap.append(
     570                                $( '<div class="wp-editor-tools">' )
     571                                        .append( $( '<div class="wp-editor-tabs">' )
     572                                                .append( $button.clone().attr({
     573                                                        id: id + '-tmce',
     574                                                        'class': 'wp-switch-editor switch-tmce'
     575                                                }).text( window.tinymce.translate( 'Visual' ) ) )
     576                                                .append( $button.attr({
     577                                                        id: id + '-html',
     578                                                        'class': 'wp-switch-editor switch-html'
     579                                                }).text( window.tinymce.translate( 'Text' ) ) )
     580                                        ).append( $editorContainer )
     581                        );
     582
     583                        $textarea.after( $wrap );
     584                        $editorContainer.append( $textarea );
     585                }
     586
     587                if ( window.tinymce && settings.tinymce ) {
     588                        if ( typeof settings.tinymce !== 'object' ) {
     589                                settings.tinymce = {};
     590                        }
     591
     592                        init = $.extend( {}, defaults.tinymce, settings.tinymce );
     593                        init.selector = '#' + id;
     594
     595                        $( document ).trigger( 'wp-before-tinymce-init', init );
     596                        window.tinymce.init( init );
     597
     598                        if ( ! window.wpActiveEditor ) {
     599                                window.wpActiveEditor = id;
     600                        }
     601                }
     602
     603                if ( window.quicktags && settings.quicktags ) {
     604                        if ( typeof settings.quicktags !== 'object' ) {
     605                                settings.quicktags = {};
     606                        }
     607
     608                        init = $.extend( {}, defaults.quicktags, settings.quicktags );
     609                        init.id = id;
     610
     611                        $( document ).trigger( 'wp-before-quicktags-init', init );
     612                        window.quicktags( init );
     613
     614                        if ( ! window.wpActiveEditor ) {
     615                                window.wpActiveEditor = init.id;
     616                        }
     617                }
     618        };
     619
     620        /**
     621         * Get the editor content.
     622         *
     623         * Intended for use with editors that were initialized with wp.editor.initialize().
     624         *
     625         * @since 4.8
     626         *
     627         * @param {string} id The HTML id of the editor textarea.
     628         * @return The editor content.
     629         */
     630        wp.editor.getContent = function( id ) {
     631                var editor;
     632
     633                if ( ! $ || ! id ) {
     634                        return;
     635                }
     636
     637                if ( window.tinymce ) {
     638                        editor = window.tinymce.get( id );
     639
     640                        if ( editor && ! editor.isHidden() ) {
     641                                editor.save();
     642                        }
     643                }
     644
     645                return $( '#' + id ).val();
     646        };
     647
     648}( window.jQuery, window.wp ));
  • src/wp-admin/js/widgets.js

    diff --git a/src/wp-admin/js/widgets.js b/src/wp-admin/js/widgets.js
    index aa439ea..77cf8f9 100644
    a b wpWidgets = { 
    4141                $(document.body).bind('click.widgets-toggle', function(e) {
    4242                        var target = $(e.target),
    4343                                css = { 'z-index': 100 },
    44                                 widget, inside, targetWidth, widgetWidth, margin;
     44                                widget, inside, targetWidth, widgetWidth, margin,
     45                                toggleBtn = target.closest( '.widget' ).find( '.widget-top button.widget-action' );
    4546
    4647                        if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) {
    4748                                widget = target.closest('div.widget');
    wpWidgets = { 
    6061                                                css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px';
    6162                                                widget.css( css );
    6263                                        }
    63                                         widget.addClass( 'open' );
    64                                         inside.slideDown('fast');
     64                                        /*
     65                                         * Don't change the order of attributes changes and animation:
     66                                         * it's important for screen readers, see ticket #31476.
     67                                         */
     68                                        toggleBtn.attr( 'aria-expanded', 'true' );
     69                                        inside.slideDown( 'fast', function() {
     70                                                widget.addClass( 'open' );
     71                                        });
    6572                                } else {
    66                                         inside.slideUp('fast', function() {
     73                                        /*
     74                                         * Don't change the order of attributes changes and animation:
     75                                         * it's important for screen readers, see ticket #31476.
     76                                         */
     77                                        toggleBtn.attr( 'aria-expanded', 'false' );
     78                                        inside.slideUp( 'fast', function() {
    6779                                                widget.attr( 'style', '' );
    6880                                                widget.removeClass( 'open' );
    6981                                        });
    wpWidgets = { 
    7890                        } else if ( target.hasClass('widget-control-close') ) {
    7991                                widget = target.closest('div.widget');
    8092                                widget.removeClass( 'open' );
     93                                toggleBtn.attr( 'aria-expanded', 'false' );
    8194                                wpWidgets.close( widget );
    8295                                e.preventDefault();
    8396                        } else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) {
    wpWidgets = { 
    92105                        wpWidgets.appendTitle( this );
    93106
    94107                        if ( $this.find( 'p.widget-error' ).length ) {
    95                                 $this.find( 'a.widget-action' ).trigger('click');
     108                                $this.find( '.widget-action' ).trigger( 'click' ).attr( 'aria-expanded', 'true' );
    96109                        }
    97110                });
    98111
    wpWidgets = { 
    182195
    183196                                if ( inside.css('display') === 'block' ) {
    184197                                        ui.item.removeClass('open');
     198                                        ui.item.find( '.widget-top button.widget-action' ).attr( 'aria-expanded', 'false' );
    185199                                        inside.hide();
    186200                                        $(this).sortable('refreshPositions');
    187201                                }
    wpWidgets = { 
    254268                                }
    255269
    256270                                if ( addNew ) {
    257                                         $widget.find( 'a.widget-action' ).trigger('click');
     271                                        $widget.find( '.widget-action' ).trigger( 'click' );
    258272                                } else {
    259273                                        wpWidgets.saveOrder( $sidebar.attr('id') );
    260274                                }
    wpWidgets = { 
    492506
    493507        close : function(widget) {
    494508                widget.children('.widget-inside').slideUp('fast', function() {
    495                         widget.attr( 'style', '' );
     509                        widget.attr( 'style', '' )
     510                                .find( '.widget-top button.widget-action' )
     511                                        .attr( 'aria-expanded', 'false' )
     512                                        .focus();
    496513                });
    497514        },
    498515
  • src/wp-admin/network/menu.php

    diff --git a/src/wp-admin/network/menu.php b/src/wp-admin/network/menu.php
    index 41281e7..e4cc786 100644
    a b  
    1919        $submenu['index.php'][10] = array( __( 'Updates' ), 'update_core', 'update-core.php' );
    2020}
    2121
    22 $submenu['index.php'][15] = array( __( 'Upgrade Network' ), 'manage_network', 'upgrade.php' );
     22$submenu['index.php'][15] = array( __( 'Upgrade Network' ), 'upgrade_network', 'upgrade.php' );
    2323
    2424$menu[4] = array( '', 'read', 'separator1', '', 'wp-menu-separator' );
    2525
  • src/wp-admin/network/site-new.php

    diff --git a/src/wp-admin/network/site-new.php b/src/wp-admin/network/site-new.php
    index 0be051e..f36dffb 100644
    a b  
    1313/** WordPress Translation Install API */
    1414require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
    1515
    16 if ( ! current_user_can( 'manage_sites' ) )
     16if ( ! current_user_can( 'create_sites' ) ) {
    1717        wp_die( __( 'Sorry, you are not allowed to add sites to this network.' ) );
     18}
    1819
    1920get_current_screen()->add_help_tab( array(
    2021        'id'      => 'overview',
  • src/wp-admin/network/upgrade.php

    diff --git a/src/wp-admin/network/upgrade.php b/src/wp-admin/network/upgrade.php
    index 5c2d3f9..46c6a94 100644
    a b  
    3232
    3333require_once( ABSPATH . 'wp-admin/admin-header.php' );
    3434
    35 if ( ! current_user_can( 'manage_network' ) )
     35if ( ! current_user_can( 'upgrade_network' ) ) {
    3636        wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 );
     37}
    3738
    3839echo '<div class="wrap">';
    3940echo '<h1>' . __( 'Upgrade Network' ) . '</h1>';
  • src/wp-admin/options-reading.php

    diff --git a/src/wp-admin/options-reading.php b/src/wp-admin/options-reading.php
    index 69c425f..baa9f75 100644
    a b  
    2222        'title'   => __('Overview'),
    2323        'content' => '<p>' . __('This screen contains the settings that affect the display of your content.') . '</p>' .
    2424                '<p>' . sprintf(__('You can choose what&#8217;s displayed on the front page of your site. It can be posts in reverse chronological order (classic blog), or a fixed/static page. To set a static home page, you first need to create two <a href="%s">Pages</a>. One will become the front page, and the other will be where your posts are displayed.'), 'post-new.php?post_type=page') . '</p>' .
    25                 '<p>' . __('You can also control the display of your content in RSS feeds, including the maximum numbers of posts to display and whether to show full text or a summary.') . '</p>' .
     25                '<p>' . __('You can also control the display of your content in RSS feeds, including the maximum number of posts to display and whether to show full text or a summary.') . '</p>' .
    2626                '<p>' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '</p>',
    2727) );
    2828
  • src/wp-admin/widgets.php

    diff --git a/src/wp-admin/widgets.php b/src/wp-admin/widgets.php
    index db4459b..ade84bb 100644
    a b  
    387387<div id="widgets-left">
    388388        <div id="available-widgets" class="widgets-holder-wrap">
    389389                <div class="sidebar-name">
    390                         <div class="sidebar-name-arrow"><br /></div>
     390                        <button type="button" class="handlediv button-link" aria-expanded="true">
     391                                <span class="screen-reader-text">Toggle panel: <?php _e( 'Available Widgets' ); ?></span>
     392                                <span class="sidebar-name-arrow" aria-hidden="true"></span>
     393                        </button>
    391394                        <h2><?php _e( 'Available Widgets' ); ?> <span id="removing-widget"><?php _ex( 'Deactivate', 'removing-widget' ); ?> <span></span></span></h2>
    392395                </div>
    393396                <div class="widget-holder">
  • src/wp-content/themes/twentyseventeen/README.txt

    diff --git a/src/wp-content/themes/twentyseventeen/README.txt b/src/wp-content/themes/twentyseventeen/README.txt
    index 5f1cfb7..9360fc7 100644
    a b  
    22Contributors: the WordPress team
    33Requires at least: WordPress 4.7
    44Tested up to: WordPress 4.7
    5 Version: 1.1
     5Version: 1.2
    66License: GPLv2 or later
    77License URI: http://www.gnu.org/licenses/gpl-2.0.html
    88Tags: one-column, two-columns, right-sidebar, flexible-header, accessibility-ready, custom-colors, custom-header, custom-menu, custom-logo, editor-style, featured-images, footer-widgets, post-formats, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready
    Source: https://unsplash.com/@englr?photo=bIhpiQA009k 
    6060
    6161== Changelog ==
    6262
     63= 1.2 =
     64* Released: April 18, 2017
     65
     66https://codex.wordpress.org/Twenty_Seventeen_Theme_Changelog#Version_1.2
     67
    6368= 1.1 =
    6469* Released: January 6, 2017
    6570
    66 - Fix incorrect $content_width value in theme
    67 - Ensure functions in customize-controls.js don't count on Customizer sections always being present
    68 - Deprecate page_home nav menu item starter content in favor of link_home
    69 - Introduce a theme-specific filter twentyseventeen_starter_content for customizing the starter content array
     71https://codex.wordpress.org/Twenty_Seventeen_Theme_Changelog#Version_1.1
    7072
    7173= 1.0 =
    7274* Released: December 6, 2016
  • src/wp-content/themes/twentyseventeen/footer.php

    diff --git a/src/wp-content/themes/twentyseventeen/footer.php b/src/wp-content/themes/twentyseventeen/footer.php
    index c21cfb7..504f573 100644
    a b  
    99 * @package WordPress
    1010 * @subpackage Twenty_Seventeen
    1111 * @since 1.0
    12  * @version 1.0
     12 * @version 1.2
    1313 */
    1414
    1515?>
  • src/wp-content/themes/twentyseventeen/style.css

    diff --git a/src/wp-content/themes/twentyseventeen/style.css b/src/wp-content/themes/twentyseventeen/style.css
    index da3791d..37ec33f 100644
    a b Theme URI: https://wordpress.org/themes/twentyseventeen/ 
    44Author: the WordPress team
    55Author URI: https://wordpress.org/
    66Description: Twenty Seventeen brings your site to life with header video and immersive featured images. With a focus on business sites, it features multiple sections on the front page as well as widgets, navigation and social menus, a logo, and more. Personalize its asymmetrical grid with a custom color scheme and showcase your multimedia content with post formats. Our default theme for 2017 works great in many languages, for any abilities, and on any device.
    7 Version: 1.1
     7Version: 1.2
    88License: GNU General Public License v2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
    1010Text Domain: twentyseventeen
    h1 { 
    451451        font-weight: 300;
    452452}
    453453
    454 h2 {
     454h2,
     455.home.blog .entry-title {
    455456        color: #666;
    456457        font-size: 20px;
    457458        font-size: 1.25rem;
    object { 
    31533154        }
    31543155
    31553156        h2,
     3157        .home.blog .entry-title,
    31563158        .page .panel-content .recent-posts .entry-title {
    31573159                font-size: 26px;
    31583160                font-size: 1.625rem;
  • src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php b/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php
    index be7e426..2c2115e 100644
    a b  
    55 * @package WordPress
    66 * @subpackage Twenty_Seventeen
    77 * @since 1.0
    8  * @version 1.0
     8 * @version 1.2
    99 */
    1010
    1111?>
  • src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php
    index cf17889..63cf2e5 100644
    a b  
    77 * @package WordPress
    88 * @subpackage Twenty_Seventeen
    99 * @since 1.0
    10  * @version 1.0
     10 * @version 1.2
    1111 */
    1212
    1313?>
     
    3333
    3434                        if ( is_single() ) {
    3535                                the_title( '<h1 class="entry-title">', '</h1>' );
     36                        } elseif ( is_front_page() && is_home() ) {
     37                                the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
    3638                        } else {
    3739                                the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
    3840                        }
  • src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php
    index 4ddc28c..a552fba 100644
    a b  
    99 * @package WordPress
    1010 * @subpackage Twenty_Seventeen
    1111 * @since 1.0
    12  * @version 1.0
     12 * @version 1.2
    1313 */
    1414
    1515?>
     
    3030                        </div><!-- .entry-meta -->
    3131                <?php endif; ?>
    3232
    33                 <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
     33                <?php if ( is_front_page() && ! is_home() ) {
     34
     35                        // The excerpt is being displayed within a front page section, so it's a lower hierarchy than h2.
     36                        the_title( sprintf( '<h3 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h3>' );
     37                } else {
     38                        the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' );
     39                } ?>
    3440        </header><!-- .entry-header -->
    3541
    3642        <div class="entry-summary">
  • src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php
    index e46e7c5..f5934e1 100644
    a b  
    77 * @package WordPress
    88 * @subpackage Twenty_Seventeen
    99 * @since 1.0
    10  * @version 1.0
     10 * @version 1.2
    1111 */
    1212
    1313?>
     
    3333
    3434                if ( is_single() ) {
    3535                        the_title( '<h1 class="entry-title">', '</h1>' );
     36                } elseif ( is_front_page() && is_home() ) {
     37                        the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
    3638                } else {
    3739                        the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
    3840                }
  • src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php
    index 8568d92..91c6545 100644
    a b  
    77 * @package WordPress
    88 * @subpackage Twenty_Seventeen
    99 * @since 1.0
    10  * @version 1.0
     10 * @version 1.2
    1111 */
    1212
    1313?>
     
    3333
    3434                        if ( is_single() ) {
    3535                                the_title( '<h1 class="entry-title">', '</h1>' );
     36                        } elseif ( is_front_page() && is_home() ) {
     37                                the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
    3638                        } else {
    3739                                the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
    3840                        }
  • src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php
    index 1c3ef49..dad681f 100644
    a b  
    77 * @package WordPress
    88 * @subpackage Twenty_Seventeen
    99 * @since 1.0
    10  * @version 1.0
     10 * @version 1.2
    1111 */
    1212
    1313?>
     
    3333
    3434                        if ( is_single() ) {
    3535                                the_title( '<h1 class="entry-title">', '</h1>' );
     36                        } elseif ( is_front_page() && is_home() ) {
     37                                the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
    3638                        } else {
    3739                                the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
    3840                        }
  • src/wp-content/themes/twentyseventeen/template-parts/post/content.php

    diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content.php
    index a87f646..90c3a4d 100644
    a b  
    77 * @package WordPress
    88 * @subpackage Twenty_Seventeen
    99 * @since 1.0
    10  * @version 1.0
     10 * @version 1.2
    1111 */
    1212
    1313?>
     
    3333
    3434                if ( is_single() ) {
    3535                        the_title( '<h1 class="entry-title">', '</h1>' );
     36                } elseif ( is_front_page() && is_home() ) {
     37                        the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
    3638                } else {
    3739                        the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
    3840                }
  • src/wp-includes/capabilities.php

    diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php
    index 0e766b1..abd87257 100644
    a b function map_meta_cap( $cap, $user_id ) { 
    478478        case 'manage_network_plugins':
    479479        case 'manage_network_themes':
    480480        case 'manage_network_options':
     481        case 'upgrade_network':
    481482                $caps[] = $cap;
    482483                break;
    483484        case 'setup_network':
  • src/wp-includes/class-walker-nav-menu.php

    diff --git a/src/wp-includes/class-walker-nav-menu.php b/src/wp-includes/class-walker-nav-menu.php
    index 4c1a1f2..c97188b 100644
    a b public function start_lvl( &$output, $depth = 0, $args = array() ) { 
    5858                        $n = "\n";
    5959                }
    6060                $indent = str_repeat( $t, $depth );
    61                 $output .= "{$n}{$indent}<ul class=\"sub-menu\">{$n}";
     61
     62                /**
     63                 * Filters the CSS class(es) applied to a menu list element.
     64                 *
     65                 * @since 4.8.0
     66                 *
     67                 * @param array    $classes The CSS classes that are applied to the menu `<ul>` element.
     68                 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
     69                 * @param int      $depth   Depth of menu item. Used for padding.
     70                 */
     71
     72                $classes = array( 'sub-menu' );
     73                $classes = apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth );
     74                $class_names = join( ' ', $classes );
     75
     76                $output .= "{$n}{$indent}<ul class='" . esc_attr( $class_names ) . "'>{$n}";
    6277        }
    6378
    6479        /**
  • src/wp-includes/class-walker-page.php

    diff --git a/src/wp-includes/class-walker-page.php b/src/wp-includes/class-walker-page.php
    index 30831bb..8bec66f 100644
    a b public function start_el( &$output, $page, $depth = 0, $args = array(), $current 
    160160                $args['link_before'] = empty( $args['link_before'] ) ? '' : $args['link_before'];
    161161                $args['link_after'] = empty( $args['link_after'] ) ? '' : $args['link_after'];
    162162
     163                $atts = array();
     164                $atts['href'] = get_permalink( $page->ID );
     165
     166                /**
     167                 * Filters the HTML attributes applied to a page menu item's anchor element.
     168                 *
     169                 * @since 4.8.0
     170                 *
     171                 * @param array $atts {
     172                 *     The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
     173                 *
     174                 *     @type string $href The href attribute.
     175                 * }
     176                 * @param WP_Post $page         Page data object.
     177                 * @param int     $depth        Depth of page, used for padding.
     178                 * @param array   $args         An array of arguments.
     179                 * @param int     $current_page ID of the current page.
     180                 */
     181                $atts = apply_filters( 'page_menu_link_attributes', $atts, $page, $depth, $args, $current_page );
     182
     183                $attributes = '';
     184                foreach ( $atts as $attr => $value ) {
     185                        if ( ! empty( $value ) ) {
     186                                $value = esc_attr( $value );
     187                                $attributes .= ' ' . $attr . '="' . $value . '"';
     188                        }
     189                }
     190
    163191                $output .= $indent . sprintf(
    164                         '<li class="%s"><a href="%s">%s%s%s</a>',
     192                        '<li class="%s"><a%s>%s%s%s</a>',
    165193                        $css_classes,
    166                         get_permalink( $page->ID ),
     194                        $attributes,
    167195                        $args['link_before'],
    168196                        /** This filter is documented in wp-includes/post-template.php */
    169197                        apply_filters( 'the_title', $page->post_title, $page->ID ),
  • src/wp-includes/class-wp-editor.php

    diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php
    index 83777cf..21353c6 100644
    a b  
    2727        private static $drag_drop_upload = false;
    2828        private static $old_dfw_compat = false;
    2929        private static $translation;
     30        private static $tinymce_scripts_printed = false;
     31        private static $link_dialog_printed = false;
    3032
    3133        private function __construct() {}
    3234
    public static function editor_settings($editor_id, $set) { 
    350352                if ( self::$this_tinymce ) {
    351353
    352354                        if ( empty( self::$first_init ) ) {
    353                                 self::$baseurl = includes_url( 'js/tinymce' );
    354 
    355                                 $mce_locale = get_user_locale();
    356                                 self::$mce_locale = $mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
    357 
    358                                 /** This filter is documented in wp-admin/includes/media.php */
    359                                 $no_captions = (bool) apply_filters( 'disable_captions', '' );
     355                                $baseurl = self::get_baseurl();
     356                                $mce_locale = self::get_mce_locale();
    360357                                $ext_plugins = '';
    361                                 $shortcut_labels = array();
    362 
    363                                 foreach ( self::get_translation() as $name => $value ) {
    364                                         if ( is_array( $value ) ) {
    365                                                 $shortcut_labels[$name] = $value[1];
    366                                         }
    367                                 }
    368358
    369359                                if ( $set['teeny'] ) {
    370360
    public static function editor_settings($editor_id, $set) { 
    376366                                         * @param array  $plugins   An array of teenyMCE plugins.
    377367                                         * @param string $editor_id Unique editor identifier, e.g. 'content'.
    378368                                         */
    379                                         self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
     369                                        $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
    380370                                } else {
    381371
    382372                                        /**
    public static function editor_settings($editor_id, $set) { 
    521511                                self::$plugins = $plugins;
    522512                                self::$ext_plugins = $ext_plugins;
    523513
    524                                 self::$first_init = array(
    525                                         'theme' => 'modern',
    526                                         'skin' => 'lightgray',
    527                                         'language' => self::$mce_locale,
    528                                         'formats' => '{' .
    529                                                 'alignleft: [' .
    530                                                         '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
    531                                                         '{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
    532                                                 '],' .
    533                                                 'aligncenter: [' .
    534                                                         '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
    535                                                         '{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
    536                                                 '],' .
    537                                                 'alignright: [' .
    538                                                         '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
    539                                                         '{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
    540                                                 '],' .
    541                                                 'strikethrough: {inline: "del"}' .
    542                                         '}',
    543                                         'relative_urls' => false,
    544                                         'remove_script_host' => false,
    545                                         'convert_urls' => false,
    546                                         'browser_spellcheck' => true,
    547                                         'fix_list_elements' => true,
    548                                         'entities' => '38,amp,60,lt,62,gt',
    549                                         'entity_encoding' => 'raw',
    550                                         'keep_styles' => false,
    551                                         'cache_suffix' => 'wp-mce-' . $tinymce_version,
    552 
    553                                         // Limit the preview styles in the menu/toolbar
    554                                         'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
    555 
    556                                         'end_container_on_empty_block' => true,
    557                                         'wpeditimage_disable_captions' => $no_captions,
    558                                         'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ),
    559                                         'plugins' => implode( ',', $plugins ),
    560                                         'wp_lang_attr' => get_bloginfo( 'language' ),
    561                                         'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ),
    562                                 );
     514                                $settings = self::default_settings();
     515                                $settings['plugins'] = implode( ',', $plugins );
    563516
    564517                                if ( ! empty( $mce_external_plugins ) ) {
    565                                         self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins );
     518                                        $settings['external_plugins'] = wp_json_encode( $mce_external_plugins );
    566519                                }
    567520
    568                                 $suffix = SCRIPT_DEBUG ? '' : '.min';
    569                                 $version = 'ver=' . get_bloginfo( 'version' );
    570                                 $dashicons = includes_url( "css/dashicons$suffix.css?$version" );
    571 
    572                                 // WordPress default stylesheet and dashicons
    573                                 $mce_css = array(
    574                                         $dashicons,
    575                                         self::$baseurl . '/skins/wordpress/wp-content.css?' . $version
    576                                 );
     521                                /** This filter is documented in wp-admin/includes/media.php */
     522                                if ( apply_filters( 'disable_captions', '' ) ) {
     523                                        $settings['wpeditimage_disable_captions'] = true;
     524                                }
    577525
     526                                $mce_css = $settings['content_css'];
    578527                                $editor_styles = get_editor_stylesheets();
     528
    579529                                if ( ! empty( $editor_styles ) ) {
    580                                         foreach ( $editor_styles as $style ) {
    581                                                 $mce_css[] = $style;
    582                                         }
     530                                        $mce_css .= ',' . implode( ',', $editor_styles );
    583531                                }
    584532
    585533                                /**
    public static function editor_settings($editor_id, $set) { 
    589537                                 *
    590538                                 * @param string $stylesheets Comma-delimited list of stylesheets.
    591539                                 */
    592                                 $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
     540                                $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
    593541
    594                                 if ( ! empty($mce_css) )
    595                                         self::$first_init['content_css'] = $mce_css;
     542                                if ( ! empty( $mce_css ) ) {
     543                                        $settings['content_css'] = $mce_css;
     544                                } else {
     545                                        unset( $settings['content_css'] );
     546                                }
     547
     548                                self::$first_init = $settings;
    596549                        }
    597550
    598551                        if ( $set['teeny'] ) {
    public static function editor_settings($editor_id, $set) { 
    690643
    691644                        $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
    692645
    693                         if ( !empty($set['tinymce']['body_class']) ) {
     646                        if ( ! empty( $set['tinymce']['body_class'] ) ) {
    694647                                $body_class .= ' ' . $set['tinymce']['body_class'];
    695                                 unset($set['tinymce']['body_class']);
     648                                unset( $set['tinymce']['body_class'] );
    696649                        }
    697650
    698651                        $mceInit = array (
    699652                                'selector' => "#$editor_id",
    700                                 'resize' => 'vertical',
    701                                 'menubar' => false,
    702653                                'wpautop' => (bool) $set['wpautop'],
    703654                                'indent' => ! $set['wpautop'],
    704                                 'toolbar1' => implode($mce_buttons, ','),
    705                                 'toolbar2' => implode($mce_buttons_2, ','),
    706                                 'toolbar3' => implode($mce_buttons_3, ','),
    707                                 'toolbar4' => implode($mce_buttons_4, ','),
     655                                'toolbar1' => implode( ',', $mce_buttons ),
     656                                'toolbar2' => implode( ',', $mce_buttons_2 ),
     657                                'toolbar3' => implode( ',', $mce_buttons_3 ),
     658                                'toolbar4' => implode( ',', $mce_buttons_4 ),
    708659                                'tabfocus_elements' => $set['tabfocus_elements'],
    709660                                'body_class' => $body_class
    710661                        );
    public static function editor_settings($editor_id, $set) { 
    762713         * @param array $init
    763714         * @return string
    764715         */
    765         private static function _parse_init($init) {
     716        private static function _parse_init( $init ) {
    766717                $options = '';
    767718
    768                 foreach ( $init as $k => $v ) {
    769                         if ( is_bool($v) ) {
    770                                 $val = $v ? 'true' : 'false';
    771                                 $options .= $k . ':' . $val . ',';
     719                foreach ( $init as $key => $value ) {
     720                        if ( is_bool( $value ) ) {
     721                                $val = $value ? 'true' : 'false';
     722                                $options .= $key . ':' . $val . ',';
    772723                                continue;
    773                         } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
    774                                 $options .= $k . ':' . $v . ',';
     724                        } elseif ( ! empty( $value ) && is_string( $value ) && (
     725                                ( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) ||
     726                                ( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) ||
     727                                preg_match( '/^\(?function ?\(/', $value ) ) ) {
     728
     729                                $options .= $key . ':' . $value . ',';
    775730                                continue;
    776731                        }
    777                         $options .= $k . ':"' . $v . '",';
     732                        $options .= $key . ':"' . $value . '",';
    778733                }
    779734
    780735                return '{' . trim( $options, ' ,' ) . '}';
    private static function _parse_init($init) { 
    784739         *
    785740         * @static
    786741         */
    787         public static function enqueue_scripts() {
    788                 if ( self::$has_tinymce )
    789                         wp_enqueue_script('editor');
     742        public static function enqueue_scripts( $default_scripts = false ) {
     743                if ( $default_scripts || self::$has_tinymce ) {
     744                        wp_enqueue_script( 'editor' );
     745                }
    790746
    791                 if ( self::$has_quicktags ) {
     747                if ( $default_scripts || self::$has_quicktags ) {
    792748                        wp_enqueue_script( 'quicktags' );
    793749                        wp_enqueue_style( 'buttons' );
    794750                }
    795751
    796                 if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
    797                         wp_enqueue_script('wplink');
     752                if ( $default_scripts || in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
     753                        wp_enqueue_script( 'wplink' );
    798754                        wp_enqueue_script( 'jquery-ui-autocomplete' );
    799755                }
    800756
    801757                if ( self::$old_dfw_compat ) {
    802                         wp_enqueue_script('wp-fullscreen-stub');
     758                        wp_enqueue_script( 'wp-fullscreen-stub' );
    803759                }
    804760
    805761                if ( self::$has_medialib ) {
    806762                        add_thickbox();
    807763                        wp_enqueue_script( 'media-upload' );
    808764                        wp_enqueue_script( 'wp-embed' );
     765                } elseif ( $default_scripts ) {
     766                        wp_enqueue_script( 'media-upload' );
    809767                }
    810768
    811769                /**
    public static function enqueue_scripts() { 
    817775                 *                       and Quicktags are being loaded.
    818776                 */
    819777                do_action( 'wp_enqueue_editor', array(
    820                         'tinymce'   => self::$has_tinymce,
    821                         'quicktags' => self::$has_quicktags,
     778                        'tinymce'   => ( $default_scripts || self::$has_tinymce ),
     779                        'quicktags' => ( $default_scripts || self::$has_quicktags ),
     780                ) );
     781        }
     782
     783        /**
     784         * Enqueue all editor scripts.
     785         * For use when the editor is going to be initialized after page load.
     786         *
     787         * @since 4.8.0
     788         */
     789        public static function enqueue_default_editor() {
     790                // We are past the point where scripts can be enqueued properly.
     791                if ( did_action( 'wp_enqueue_editor' ) ) {
     792                        return;
     793                }
     794
     795                self::enqueue_scripts( true );
     796
     797                // Also add wp-includes/css/editor.css
     798                wp_enqueue_style( 'editor-buttons' );
     799
     800                if ( is_admin() ) {
     801                        add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
     802                } else {
     803                        add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
     804                }
     805        }
     806
     807        /**
     808         * Print (output) all editor scripts and default settings.
     809         * For use when the editor is going to be initialized after page load.
     810         *
     811         * @since 4.8.0
     812         *
     813         */
     814        public static function print_default_editor_scripts() {
     815                $settings = self::default_settings();
     816
     817                $settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
     818                $settings['wpautop'] = false;
     819                $settings['indent'] = true;
     820                $settings['elementpath'] = false;
     821
     822                // In production all plugins are loaded (they are in wp-editor.js.gz)
     823                // but only these will be initialized by default.
     824                $settings['plugins'] = implode( ',', array(
     825                        'charmap',
     826                        'colorpicker',
     827                        'hr',
     828                        'lists',
     829        //              'media',
     830                        'paste',
     831                        'tabfocus',
     832                        'textcolor',
     833                        'fullscreen',
     834                        'wordpress',
     835                        'wpautoresize',
     836                        'wpeditimage',
     837                        'wpemoji',
     838                        'wpgallery',
     839                        'wplink',
     840        //              'wpdialogs',
     841                        'wptextpattern',
     842        //              'wpview',
    822843                ) );
     844
     845                $settings = self::_parse_init( $settings );
     846
     847                $suffix = SCRIPT_DEBUG ? '' : '.min';
     848                $baseurl = self::get_baseurl();
     849
     850                ?>
     851                <script type="text/javascript">
     852                window.wp = window.wp || {};
     853                window.wp.editor = window.wp.editor || {};
     854                window.wp.editor.getDefaultSettings = function() {
     855                        return {
     856                                tinymce: <?php echo $settings; ?>,
     857                                quicktags: {
     858                                        buttons: 'strong,em,link,ul,ol,li,code'
     859                                }
     860                        };
     861                };
     862
     863                var tinyMCEPreInit = {
     864                        baseURL: "<?php echo $baseurl; ?>",
     865                        suffix: "<?php echo $suffix; ?>",
     866                        mceInit: {},
     867                        qtInit: {},
     868                        load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
     869                };
     870                </script>
     871                <?php
     872
     873                self::print_tinymce_scripts();
     874
     875                /**
     876                 * Fires when the editor scripts are loaded for later initialization,
     877                 * after all scripts and settings are printed.
     878                 *
     879                 * @since 4.8.0
     880                 */
     881                do_action( 'print_default_editor_scripts' );
     882
     883                self::wp_link_dialog();
     884        }
     885
     886        public static function get_mce_locale() {
     887                if ( empty( self::$mce_locale ) ) {
     888                        $mce_locale = get_user_locale();
     889                        self::$mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
     890                }
     891
     892                return self::$mce_locale;
     893        }
     894
     895        public static function get_baseurl() {
     896                if ( empty( self::$baseurl ) ) {
     897                        self::$baseurl = includes_url( 'js/tinymce' );
     898                }
     899
     900                return self::$baseurl;
     901        }
     902
     903        /**
     904         * Returns the default TinyMCE settings.
     905         * Doesn't include plugins, buttons, editor selector.
     906         *
     907         * @return array
     908         */
     909        private static function default_settings() {
     910                global $tinymce_version;
     911
     912                $shortcut_labels = array();
     913
     914                foreach ( self::get_translation() as $name => $value ) {
     915                        if ( is_array( $value ) ) {
     916                                $shortcut_labels[$name] = $value[1];
     917                        }
     918                }
     919
     920                $settings = array(
     921                        'theme' => 'modern',
     922                        'skin' => 'lightgray',
     923                        'language' => self::get_mce_locale(),
     924                        'formats' => '{' .
     925                                'alignleft: [' .
     926                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
     927                                        '{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
     928                                '],' .
     929                                'aligncenter: [' .
     930                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
     931                                        '{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
     932                                '],' .
     933                                'alignright: [' .
     934                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
     935                                        '{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
     936                                '],' .
     937                                'strikethrough: {inline: "del"}' .
     938                        '}',
     939                        'relative_urls' => false,
     940                        'remove_script_host' => false,
     941                        'convert_urls' => false,
     942                        'browser_spellcheck' => true,
     943                        'fix_list_elements' => true,
     944                        'entities' => '38,amp,60,lt,62,gt',
     945                        'entity_encoding' => 'raw',
     946                        'keep_styles' => false,
     947                        'cache_suffix' => 'wp-mce-' . $tinymce_version,
     948                        'resize' => 'vertical',
     949                        'menubar' => false,
     950
     951                        // Limit the preview styles in the menu/toolbar
     952                        'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
     953
     954                        'end_container_on_empty_block' => true,
     955                        'wpeditimage_html5_captions' => true,
     956                        'wp_lang_attr' => get_bloginfo( 'language' ),
     957                        'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ),
     958                );
     959
     960                $suffix = SCRIPT_DEBUG ? '' : '.min';
     961                $version = 'ver=' . get_bloginfo( 'version' );
     962
     963                // Default stylesheets
     964                $settings['content_css'] = includes_url( "css/dashicons$suffix.css?$version" ) . ',' .
     965                        includes_url( "js/tinymce/skins/wordpress/wp-content.css?$version" );
     966
     967                return $settings;
    823968        }
    824969
    825970        private static function get_translation() {
    private static function get_translation() { 
    10281173
    10291174                        /* translators: word count */
    10301175                        'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
    1031                         'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" . __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
    1032                         'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __( 'Rich Text Area. Press Alt-Shift-H for help.' ),
     1176                        'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' =>
     1177                                __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" .
     1178                                __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
     1179                        'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' =>
     1180                                __( 'Rich Text Area. Press Alt-Shift-H for help.' ),
    10331181                        'Rich Text Area. Press Control-Option-H for help.' => __( 'Rich Text Area. Press Control-Option-H for help.' ),
    1034                         'You have unsaved changes are you sure you want to navigate away?' => __( 'The changes you made will be lost if you navigate away from this page.' ),
    1035                         'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
     1182                        'You have unsaved changes are you sure you want to navigate away?' =>
     1183                                __( 'The changes you made will be lost if you navigate away from this page.' ),
     1184                        'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' =>
     1185                                __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
    10361186
    10371187                        // TinyMCE menus
    10381188                        'Insert' => _x( 'Insert', 'TinyMCE menu' ),
    private static function get_translation() { 
    10551205                        'Paste URL or type to search' => __( 'Paste URL or type to search' ), // Placeholder for the inline link dialog
    10561206                        'Apply'  => __( 'Apply' ), // Tooltip for the 'apply' button in the inline link dialog
    10571207                        'Link options'  => __( 'Link options' ), // Tooltip for the 'link options' button in the inline link dialog
     1208                        'Visual' => __( 'Visual' ), // Editor switch tab label
     1209                        'Text' => __( 'Text' ), // Editor switch tab label
    10581210
    10591211                        // Shortcuts help modal
    10601212                        'Keyboard Shortcuts' => array( __( 'Keyboard Shortcuts' ), 'accessH' ),
    private static function get_translation() { 
    10981250        }
    10991251
    11001252        /**
    1101          * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n().
    1102          * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object.
     1253         * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n(),
     1254         * or as JS snippet that should run after tinymce.js is loaded.
    11031255         *
    11041256         * @static
    11051257         * @param string $mce_locale The locale used for the editor.
    private static function get_translation() { 
    11081260         */
    11091261        public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
    11101262                if ( ! $mce_locale ) {
    1111                         $mce_locale = self::$mce_locale;
     1263                        $mce_locale = self::get_mce_locale();
    11121264                }
    11131265
    11141266                $mce_translation = self::get_translation();
    1115                
     1267
    11161268                foreach ( $mce_translation as $name => $value ) {
    11171269                        if ( is_array( $value ) ) {
    11181270                                $mce_translation[$name] = $value[0];
    public static function wp_mce_translation( $mce_locale = '', $json_only = false 
    11501302                        return wp_json_encode( $mce_translation );
    11511303                }
    11521304
    1153                 $baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' );
     1305                $baseurl = self::get_baseurl();
    11541306
    11551307                return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
    11561308                        "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
    11571309        }
    11581310
    11591311        /**
     1312         * Print (output) the main TinyMCE scripts.
     1313         *
     1314         * @since 4.8
    11601315         *
    11611316         * @static
    11621317         * @global string $tinymce_version
    11631318         * @global bool   $concatenate_scripts
    11641319         * @global bool   $compress_scripts
    11651320         */
    1166         public static function editor_js() {
     1321        public static function print_tinymce_scripts() {
    11671322                global $tinymce_version, $concatenate_scripts, $compress_scripts;
    11681323
    1169                 /**
    1170                  * Filters "tiny_mce_version" is deprecated
    1171                  *
    1172                  * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
    1173                  * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
    1174                  * If the plugin has a popup dialog, a query string can be added to the button action that opens it (in the plugin's code).
    1175                  */
    1176                 $version = 'ver=' . $tinymce_version;
    1177                 $tmce_on = !empty(self::$mce_settings);
     1324                if ( self::$tinymce_scripts_printed ) {
     1325                        return;
     1326                }
     1327
     1328                self::$tinymce_scripts_printed = true;
    11781329
    1179                 if ( ! isset($concatenate_scripts) )
     1330                if ( ! isset( $concatenate_scripts ) ) {
    11801331                        script_concat_settings();
     1332                }
     1333
     1334                $suffix = SCRIPT_DEBUG ? '' : '.min';
     1335                $version = 'ver=' . $tinymce_version;
     1336                $baseurl = self::get_baseurl();
    11811337
    11821338                $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
    11831339                        && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
    11841340
     1341                // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
     1342                $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
     1343
     1344                if ( $compressed ) {
     1345                        echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
     1346                } else {
     1347                        echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
     1348                        echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
     1349                }
     1350
     1351                echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
     1352        }
     1353
     1354        /**
     1355         * Print (output) the TinyMCE configuration and initialization scripts.
     1356         *
     1357         * @static
     1358         */
     1359        public static function editor_js() {
     1360                global $tinymce_version;
     1361
     1362                $tmce_on = ! empty( self::$mce_settings );
    11851363                $mceInit = $qtInit = '';
     1364
    11861365                if ( $tmce_on ) {
    11871366                        foreach ( self::$mce_settings as $editor_id => $init ) {
    11881367                                $options = self::_parse_init( $init );
    11891368                                $mceInit .= "'$editor_id':{$options},";
    11901369                        }
    1191                         $mceInit = '{' . trim($mceInit, ',') . '}';
     1370                        $mceInit = '{' . trim( $mceInit, ',' ) . '}';
    11921371                } else {
    11931372                        $mceInit = '{}';
    11941373                }
    11951374
    1196                 if ( !empty(self::$qt_settings) ) {
     1375                if ( ! empty( self::$qt_settings ) ) {
    11971376                        foreach ( self::$qt_settings as $editor_id => $init ) {
    11981377                                $options = self::_parse_init( $init );
    11991378                                $qtInit .= "'$editor_id':{$options},";
    12001379                        }
    1201                         $qtInit = '{' . trim($qtInit, ',') . '}';
     1380                        $qtInit = '{' . trim( $qtInit, ',' ) . '}';
    12021381                } else {
    12031382                        $qtInit = '{}';
    12041383                }
    public static function editor_js() { 
    12101389                );
    12111390
    12121391                $suffix = SCRIPT_DEBUG ? '' : '.min';
     1392                $baseurl = self::get_baseurl();
     1393                $version = 'ver=' . $tinymce_version;
    12131394
    12141395                /**
    12151396                 * Fires immediately before the TinyMCE settings are printed.
    public static function editor_js() { 
    12231404
    12241405                <script type="text/javascript">
    12251406                tinyMCEPreInit = {
    1226                         baseURL: "<?php echo self::$baseurl; ?>",
     1407                        baseURL: "<?php echo $baseurl; ?>",
    12271408                        suffix: "<?php echo $suffix; ?>",
    12281409                        <?php
    12291410
    public static function editor_js() { 
    12401421                </script>
    12411422                <?php
    12421423
    1243                 $baseurl = self::$baseurl;
    1244                 // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
    1245                 $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
    1246 
    12471424                if ( $tmce_on ) {
    1248                         if ( $compressed ) {
    1249                                 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
    1250                         } else {
    1251                                 echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
    1252                                 echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
    1253                         }
    1254 
    1255                         echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
     1425                        self::print_tinymce_scripts();
    12561426
    12571427                        if ( self::$ext_plugins ) {
    12581428                                // Load the old-format English strings to prevent unsightly labels in old style popups
    public static function wp_link_query( $args = array() ) { 
    14341604         * @static
    14351605         */
    14361606        public static function wp_link_dialog() {
     1607                // Run once
     1608                if ( self::$link_dialog_printed ) {
     1609                        return;
     1610                }
     1611
     1612                self::$link_dialog_printed = true;
     1613
    14371614                // display: none is required here, see #WP27605
    14381615                ?>
    14391616                <div id="wp-link-backdrop" style="display: none"></div>
  • src/wp-includes/class-wp-site.php

    diff --git a/src/wp-includes/class-wp-site.php b/src/wp-includes/class-wp-site.php
    index c16f498..4bca299 100644
    a b public function __get( $key ) { 
    240240                        case 'siteurl':
    241241                        case 'post_count':
    242242                        case 'home':
     243                        default: // Custom properties added by 'site_details' filter.
    243244                                if ( ! did_action( 'ms_loaded' ) ) {
    244245                                        return null;
    245246                                }
     247
    246248                                $details = $this->get_details();
    247                                 return $details->$key;
     249                                if ( isset( $details->$key ) ) {
     250                                        return $details->$key;
     251                                }
    248252                }
    249253
    250254                return null;
    public function __isset( $key ) { 
    275279                                        return false;
    276280                                }
    277281                                return true;
     282                        default: // Custom properties added by 'site_details' filter.
     283                                if ( ! did_action( 'ms_loaded' ) ) {
     284                                        return false;
     285                                }
     286
     287                                $details = $this->get_details();
     288                                if ( isset( $details->$key ) ) {
     289                                        return true;
     290                                }
    278291                }
    279292
    280293                return false;
  • src/wp-includes/css/admin-bar.css

    diff --git a/src/wp-includes/css/admin-bar.css b/src/wp-includes/css/admin-bar.css
    index fff096d..523b5f7 100644
    a b html:lang(he-il) .rtl #wpadminbar * { 
    624624        -moz-osx-font-smoothing: grayscale;
    625625}
    626626
     627/* The admin bar search field needs to reset many styles that might be inherited from the active Theme CSS. See ticket #40313. */
    627628#wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input {
     629        display: inline-block;
     630        float: none;
    628631        position: relative;
    629632        z-index: 30;
    630633        font-size: 13px;
    631634        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
    632635        line-height: 24px;
     636        text-indent: 0;
    633637        height: 24px;
    634638        width: 24px;
    635639        max-width: none;
  • src/wp-includes/css/buttons.css

    diff --git a/src/wp-includes/css/buttons.css b/src/wp-includes/css/buttons.css
    index 1a614cb..80ecd07 100644
    a b TABLE OF CONTENTS: 
    242242
    243243.wp-core-ui .button-link-delete:hover,
    244244.wp-core-ui .button-link-delete:focus {
    245         color: #f00;
     245        color: #dc3232;
    246246}
    247247
    248248.ie8 .wp-core-ui .button-link:focus {
    TABLE OF CONTENTS: 
    411411                vertical-align: inherit;
    412412        }
    413413
     414        .media-modal-content .media-toolbar-primary .media-button {
     415                margin-top: 10px;
     416                margin-left: 5px;
     417        }
     418
    414419        /* Reset responsive styles on Log in button on iframed login form */
    415420
    416421        .interim-login .button.button-large {
  • src/wp-includes/css/editor.css

    diff --git a/src/wp-includes/css/editor.css b/src/wp-includes/css/editor.css
    index e04317e..ca23b91 100644
    a b div.mce-path { 
    608608}
    609609
    610610/* Menubar */
    611 .mce-menubar {
     611div.mce-menubar {
    612612        border-color: #e5e5e5;
    613613        background: #fff;
    614614        border-width: 0px 0px 1px;
    615615}
    616616
    617 .mce-menubar .mce-menubtn {
    618         margin: 2px;
    619 }
    620 
    621617.mce-menubar .mce-menubtn:hover,
    622618.mce-menubar .mce-menubtn.mce-active,
    623619.mce-menubar .mce-menubtn:focus {
    i.mce-i-wp_code:before { 
    10911087
    10921088.wp-editor-container {
    10931089        clear: both;
     1090        border: 1px solid #e5e5e5;
    10941091}
    10951092
    10961093.wp-editor-area {
  • src/wp-includes/css/media-views.css

    diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css
    index 698ea2d..0c0bd6d 100644
    a b  
    242242        width: calc(48% - 12px);
    243243}
    244244
    245 .media-modal-content .attachments-browser .media-toolbar-secondary {
    246         width: 66%;
    247 }
    248 
    249 .media-modal-content .media-toolbar-primary.search-form {
    250         width: 33%;
    251 }
    252 
    253245.media-modal-content .media-toolbar-primary .media-button {
    254246        float: right;
    255247}
     
    279271        overflow: hidden;
    280272}
    281273
     274.media-frame-toolbar .media-toolbar {
     275        top: initial;
     276        bottom: -45px;
     277        height: auto;
     278        overflow: initial;
     279        border-top: 1px solid #ddd;
     280}
     281
     282@media screen and (max-width: 782px) {
     283        .media-frame-toolbar .media-toolbar {
     284                bottom: -48px;
     285        }
     286}
     287
    282288.media-toolbar-primary {
    283289        float: right;
    284290        height: 100%;
     
    686692        overflow: auto;
    687693        background: #fff;
    688694        border-top: 1px solid #ddd;
    689         border-bottom: 1px solid #ddd;
    690695}
    691696
    692697.media-frame-toolbar {
     
    696701        bottom: 0;
    697702        height: 60px;
    698703        z-index: 100;
     704        bottom: 60px;
     705        height: auto;
    699706}
    700707
    701708.media-frame.hide-menu .media-frame-title,
  • src/wp-includes/customize/class-wp-customize-color-control.php

    diff --git a/src/wp-includes/customize/class-wp-customize-color-control.php b/src/wp-includes/customize/class-wp-customize-color-control.php
    index 7f6a9b5..775a63d 100644
    a b public function render_content() {} 
    9292         */
    9393        public function content_template() {
    9494                ?>
    95                 <# var defaultValue = '',
     95                <# var defaultValue = '#RRGGBB', defaultValueAttr = '',
    9696                        isHueSlider = data.mode === 'hue';
    9797
    9898                if ( data.defaultValue && ! isHueSlider ) {
    public function content_template() { 
    101101                        } else {
    102102                                defaultValue = data.defaultValue;
    103103                        }
    104                         defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
     104                        defaultValueAttr = ' data-default-color=' + defaultValue; // Quotes added automatically.
    105105                } #>
    106106                <label>
    107107                        <# if ( data.label ) { #>
    public function content_template() { 
    114114                                <# if ( isHueSlider ) { #>
    115115                                        <input class="color-picker-hue" type="text" data-type="hue" />
    116116                                <# } else { #>
    117                                         <input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} />
     117                                        <input class="color-picker-hex" type="text" maxlength="7" placeholder="{{ defaultValue }}" {{ defaultValueAttr }} />
    118118                                <# } #>
    119119                        </div>
    120120                </label>
  • src/wp-includes/general-template.php

    diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php
    index 04df698..2930c54 100644
    a b function wp_editor( $content, $editor_id, $settings = array() ) { 
    30573057}
    30583058
    30593059/**
     3060 * Outputs the editor scripts, stylesheets, and default settings.
     3061 *
     3062 * The editor can be initialized when needed after page load.
     3063 * See wp.editor.initialize() in wp-admin/js/editor.js for initialization options.
     3064 *
     3065 * @uses _WP_Editors
     3066 * @since 4.8.0
     3067 */
     3068function wp_enqueue_editor() {
     3069        if ( ! class_exists( '_WP_Editors', false ) ) {
     3070                require( ABSPATH . WPINC . '/class-wp-editor.php' );
     3071        }
     3072
     3073        _WP_Editors::enqueue_default_editor();
     3074}
     3075
     3076/**
    30603077 * Retrieves the contents of the search WordPress query variable.
    30613078 *
    30623079 * The search query string is passed through esc_attr() to ensure that it is safe
  • src/wp-includes/js/mce-view.js

    diff --git a/src/wp-includes/js/mce-view.js b/src/wp-includes/js/mce-view.js
    index 21b2c87..397af63 100644
    a b  
    729729                                        $( node ).data( 'rendered', false );
    730730                                        editor.dom.setAttrib( node, 'data-wpview-text', encodeURIComponent( text ) );
    731731                                        wp.mce.views.createInstance( type, text, match.options, force ).render();
     732
     733                                        editor.selection.select( node );
     734                                        editor.nodeChanged();
    732735                                        editor.focus();
    733736
    734737                                        return true;
  • src/wp-includes/js/swfupload/handlers.js

    diff --git a/src/wp-includes/js/swfupload/handlers.js b/src/wp-includes/js/swfupload/handlers.js
    index e27bd37..8251f05 100644
    a b function cancelUpload() { 
    356356
    357357// remember the last used image size, alignment and url
    358358jQuery(document).ready(function($){
    359         $('input[type="radio"]', '#media-items').live('click', function(){
     359        $( 'input[type="radio"]', '#media-items' ).on( 'click', function(){
    360360                var tr = $(this).closest('tr');
    361361
    362362                if ( $(tr).hasClass('align') )
    jQuery(document).ready(function($){ 
    365365                        setUserSetting('imgsize', $(this).val());
    366366        });
    367367
    368         $('button.button', '#media-items').live('click', function(){
     368        $( 'button.button', '#media-items' ).on( 'click', function(){
    369369                var c = this.className || '';
    370370                c = c.match(/url([^ '"]+)/);
    371371                if ( c && c[1] ) {
  • src/wp-includes/js/tinymce/plugins/wordpress/plugin.js

    diff --git a/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js b/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js
    index 20cdcd4..08e3cf7 100644
    a b tinymce.PluginManager.add( 'wordpress', function( editor ) { 
    992992
    993993                function hide( event ) {
    994994                        if ( activeToolbar ) {
    995                                 if ( activeToolbar.tempHide || event.type === 'hide' ) {
     995                                if ( activeToolbar.tempHide || event.type === 'hide' || event.type === 'blur' ) {
    996996                                        activeToolbar.hide();
    997997                                        activeToolbar = false;
    998998                                } else if ( (
  • src/wp-includes/js/wp-a11y.js

    diff --git a/src/wp-includes/js/wp-a11y.js b/src/wp-includes/js/wp-a11y.js
    index 2507cbc..7ccdd75 100644
    a b window.wp = window.wp || {}; 
    44        'use strict';
    55
    66        var $containerPolite,
    7                 $containerAssertive;
     7                $containerAssertive,
     8                previousMessage = '';
    89
    910        /**
    1011         * Update the ARIA live notification area text node.
    window.wp = window.wp || {}; 
    2324                // Ensure only text is sent to screen readers.
    2425                message = $( '<p>' ).html( message ).text();
    2526
     27                /*
     28                 * Safari 10+VoiceOver don't announce repeated, identical strings. We use
     29                 * a `no-break space` to force them to think identical strings are different.
     30                 * See ticket #36853.
     31                 */
     32                if ( previousMessage === message ) {
     33                        message = message + '\u00A0';
     34                }
     35
     36                previousMessage = message;
     37
    2638                if ( $containerAssertive && 'assertive' === ariaLive ) {
    2739                        $containerAssertive.text( message );
    2840                } else if ( $containerPolite ) {
  • src/wp-includes/media.php

    diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
    index 195d0f9..6253bd5 100644
    a b function wp_enqueue_media( $args = array() ) { 
    33243324        /**
    33253325         * Allows showing or hiding the "Create Audio Playlist" button in the media library.
    33263326         *
    3327          * By default (if this filter returns `null`), a query will be run to
    3328          * determine whether the media library contains any audio items.  This
    3329          * query is expensive for large media libraries, so it may be desirable for
    3330          * sites to override this behavior.
     3327         * By default, the "Create Audio Playlist" button will always be shown in
     3328         * the media library.  If this filter returns `null`, a query will be run
     3329         * to determine whether the media library contains any audio items.  This
     3330         * was the default behavior prior to version 4.8.0, but this query is
     3331         * expensive for large media libraries.
    33313332         *
    33323333         * @since 4.7.4
     3334         * @since 4.8.0 The filter's default value is `true` rather than `null`.
    33333335         *
    33343336         * @link https://core.trac.wordpress.org/ticket/31071
    33353337         *
    3336          * @return bool|null Whether to show the button, or `null` for default behavior.
     3338         * @param bool|null Whether to show the button, or `null` to decide based
     3339         *                  on whether any audio files exist in the media library.
    33373340         */
    3338         $has_audio = apply_filters( 'media_has_audio', null );
    3339         if ( is_null( $has_audio ) ) {
    3340                 $has_audio = $wpdb->get_var( "
     3341        $show_audio_playlist = apply_filters( 'media_library_show_audio_playlist', true );
     3342        if ( null === $show_audio_playlist ) {
     3343                $show_audio_playlist = $wpdb->get_var( "
    33413344                        SELECT ID
    33423345                        FROM $wpdb->posts
    33433346                        WHERE post_type = 'attachment'
    function wp_enqueue_media( $args = array() ) { 
    33493352        /**
    33503353         * Allows showing or hiding the "Create Video Playlist" button in the media library.
    33513354         *
    3352          * By default (if this filter returns `null`), a query will be run to
    3353          * determine whether the media library contains any video items.  This
    3354          * query is expensive for large media libraries, so it may be desirable for
    3355          * sites to override this behavior.
     3355         * By default, the "Create Video Playlist" button will always be shown in
     3356         * the media library.  If this filter returns `null`, a query will be run
     3357         * to determine whether the media library contains any video items.  This
     3358         * was the default behavior prior to version 4.8.0, but this query is
     3359         * expensive for large media libraries.
    33563360         *
    33573361         * @since 4.7.4
     3362         * @since 4.8.0 The filter's default value is `true` rather than `null`.
    33583363         *
    33593364         * @link https://core.trac.wordpress.org/ticket/31071
    33603365         *
    3361          * @return bool|null Whether to show the button, or `null` for default behavior.
     3366         * @param bool|null Whether to show the button, or `null` to decide based
     3367         *                  on whether any video files exist in the media library.
    33623368         */
    3363         $has_video = apply_filters( 'media_has_video', null );
    3364         if ( is_null( $has_video ) ) {
    3365                 $has_video = $wpdb->get_var( "
     3369        $show_video_playlist = apply_filters( 'media_library_show_video_playlist', true );
     3370        if ( null === $show_video_playlist ) {
     3371                $show_video_playlist = $wpdb->get_var( "
    33663372                        SELECT ID
    33673373                        FROM $wpdb->posts
    33683374                        WHERE post_type = 'attachment'
    function wp_enqueue_media( $args = array() ) { 
    33833389         *
    33843390         * @link https://core.trac.wordpress.org/ticket/31071
    33853391         *
    3386          * @return array|null An array of objects with `month` and `year`
    3387          *                    properties, or `null` (or any other non-array value)
    3388          *                    for default behavior.
     3392         * @param array|null An array of objects with `month` and `year`
     3393         *                   properties, or `null` (or any other non-array value)
     3394         *                   for default behavior.
    33893395         */
    3390         $months = apply_filters( 'media_months', null );
     3396        $months = apply_filters( 'media_library_months_with_files', null );
    33913397        if ( ! is_array( $months ) ) {
    33923398                $months = $wpdb->get_results( $wpdb->prepare( "
    33933399                        SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
    function wp_enqueue_media( $args = array() ) { 
    34143420                ),
    34153421                'defaultProps' => $props,
    34163422                'attachmentCounts' => array(
    3417                         'audio' => ( $has_audio ) ? 1 : 0,
    3418                         'video' => ( $has_video ) ? 1 : 0
     3423                        'audio' => ( $show_audio_playlist ) ? 1 : 0,
     3424                        'video' => ( $show_video_playlist ) ? 1 : 0,
    34193425                ),
    34203426                'embedExts'    => $exts,
    34213427                'embedMimes'   => $ext_mimes,
    34223428                'contentWidth' => $content_width,
    34233429                'months'       => $months,
    3424                 'mediaTrash'   => MEDIA_TRASH ? 1 : 0
     3430                'mediaTrash'   => MEDIA_TRASH ? 1 : 0,
    34253431        );
    34263432
    34273433        $post = null;
  • src/wp-includes/ms-functions.php

    diff --git a/src/wp-includes/ms-functions.php b/src/wp-includes/ms-functions.php
    index 9df86cf..d209435 100644
    a b function wp_schedule_update_network_counts() { 
    22742274 * Update the network-wide counts for the current network.
    22752275 *
    22762276 * @since 3.1.0
     2277 * @since 4.8.0 The $network_id parameter has been added.
     2278 *
     2279 * @param int|null $network_id ID of the network. Default is the current network.
    22772280 */
    2278 function wp_update_network_counts() {
    2279         wp_update_network_user_counts();
    2280         wp_update_network_site_counts();
     2281function wp_update_network_counts( $network_id = null ) {
     2282        wp_update_network_user_counts( $network_id );
     2283        wp_update_network_site_counts( $network_id );
    22812284}
    22822285
    22832286/**
    function wp_maybe_update_network_user_counts() { 
    23292332 * Update the network-wide site count.
    23302333 *
    23312334 * @since 3.7.0
     2335 * @since 4.8.0 The $network_id parameter has been added.
    23322336 *
    2333  * @global wpdb $wpdb WordPress database abstraction object.
     2337 * @param int|null $network_id ID of the network. Default is the current network.
    23342338 */
    2335 function wp_update_network_site_counts() {
    2336         global $wpdb;
     2339function wp_update_network_site_counts( $network_id = null ) {
     2340        $network_id = (int) $network_id;
     2341        if ( ! $network_id ) {
     2342                $network_id = get_current_network_id();
     2343        }
    23372344
    23382345        $count = get_sites( array(
    2339                 'network_id' => $wpdb->siteid,
     2346                'network_id' => $network_id,
    23402347                'spam'       => 0,
    23412348                'deleted'    => 0,
    23422349                'archived'   => 0,
    23432350                'count'      => true,
    23442351        ) );
    23452352
    2346         update_site_option( 'blog_count', $count );
     2353        update_network_option( $network_id, 'blog_count', $count );
    23472354}
    23482355
    23492356/**
    23502357 * Update the network-wide user count.
    23512358 *
    23522359 * @since 3.7.0
     2360 * @since 4.8.0 The $network_id parameter has been added.
    23532361 *
    23542362 * @global wpdb $wpdb WordPress database abstraction object.
     2363 *
     2364 * @param int|null $network_id ID of the network. Default is the current network.
    23552365 */
    2356 function wp_update_network_user_counts() {
     2366function wp_update_network_user_counts( $network_id = null ) {
    23572367        global $wpdb;
    23582368
    23592369        $count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
    2360         update_site_option( 'user_count', $count );
     2370        update_network_option( $network_id, 'user_count', $count );
    23612371}
    23622372
    23632373/**
  • src/wp-includes/option.php

    diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php
    index d58ae69..8bc8edc 100644
    a b function get_site_transient( $transient ) { 
    16411641 * @see set_transient()
    16421642 *
    16431643 * @param string $transient  Transient name. Expected to not be SQL-escaped. Must be
    1644  *                           40 characters or fewer in length.
     1644 *                           167 characters or fewer in length.
    16451645 * @param mixed  $value      Transient value. Expected to not be SQL-escaped.
    16461646 * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
    16471647 * @return bool False if value was not set and true if value was set.
  • src/wp-includes/post.php

    diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php
    index a33236c..0d62400 100644
    a b function register_post_status( $post_status, $args = array() ) { 
    815815                $args->label = $post_status;
    816816
    817817        if ( false === $args->label_count )
    818                 $args->label_count = array( $args->label, $args->label );
     818                $args->label_count = _n_noop( $args->label, $args->label );
    819819
    820820        $wp_post_statuses[$post_status] = $args;
    821821
    function wp_untrash_post_comments( $post = null ) { 
    28002800 *
    28012801 * @param int   $post_id Optional. The Post ID. Does not default to the ID of the
    28022802 *                       global $post. Default 0.
    2803  * @param array $args    Optional. Category arguments. See wp_get_object_terms(). Default empty.
    2804  * @return array List of categories. If the `$fields` argument passed via `$args` is 'all' or
    2805  *               'all_with_object_id', an array of WP_Term objects will be returned. If `$fields`
    2806  *               is 'ids', an array of category ids. If `$fields` is 'names', an array of category names.
     2803 * @param array $args    Optional. Category query parameters. Default empty array.
     2804 *                       See WP_Term_Query::__construct() for supported arguments.
     2805 * @return array|WP_Error List of categories. If the `$fields` argument passed via `$args` is 'all' or
     2806 *                        'all_with_object_id', an array of WP_Term objects will be returned. If `$fields`
     2807 *                        is 'ids', an array of category ids. If `$fields` is 'names', an array of category names.
     2808 *                        WP_Error object if 'category' taxonomy doesn't exist.
    28072809 */
    28082810function wp_get_post_categories( $post_id = 0, $args = array() ) {
    28092811        $post_id = (int) $post_id;
    function wp_get_post_categories( $post_id = 0, $args = array() ) { 
    28262828 *
    28272829 * @param int   $post_id Optional. The Post ID. Does not default to the ID of the
    28282830 *                       global $post. Default 0.
    2829  * @param array $args Optional. Overwrite the defaults
    2830  * @return array List of post tags.
     2831 * @param array $args    Optional. Tag query parameters. Default empty array.
     2832 *                       See WP_Term_Query::__construct() for supported arguments.
     2833 * @return array|WP_Error Array of WP_Term objects on success or empty array if no tags were found.
     2834 *                        WP_Error object if 'post_tag' taxonomy doesn't exist.
    28312835 */
    28322836function wp_get_post_tags( $post_id = 0, $args = array() ) {
    28332837        return wp_get_post_terms( $post_id, 'post_tag', $args);
    function wp_get_post_tags( $post_id = 0, $args = array() ) { 
    28452849 * @param int    $post_id  Optional. The Post ID. Does not default to the ID of the
    28462850 *                         global $post. Default 0.
    28472851 * @param string $taxonomy Optional. The taxonomy for which to retrieve terms. Default 'post_tag'.
    2848  * @param array  $args     Optional. wp_get_object_terms() arguments. Default empty array.
    2849  * @return array|WP_Error  List of post terms or empty array if no terms were found. WP_Error object
    2850  *                         if `$taxonomy` doesn't exist.
     2852 * @param array  $args     Optional. Term query parameters. Default empty array.
     2853 *                         See WP_Term_Query::__construct() for supported arguments.
     2854 * @return array|WP_Error  Array of WP_Term objects on success or empty array if no terms were found.
     2855 *                         WP_Error object if `$taxonomy` doesn't exist.
    28512856 */
    28522857function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
    28532858        $post_id = (int) $post_id;
  • src/wp-includes/taxonomy.php

    diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php
    index 94f3668..f1c1bce 100644
    a b function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { 
    18411841
    18421842        $args = wp_parse_args( $args );
    18431843
     1844        /*
     1845         * When one or more queried taxonomies is registered with an 'args' array,
     1846         * those params override the `$args` passed to this function.
     1847         */
     1848        $terms = array();
     1849        if ( count( $taxonomies ) > 1 ) {
     1850                foreach ( $taxonomies as $index => $taxonomy ) {
     1851                        $t = get_taxonomy( $taxonomy );
     1852                        if ( isset( $t->args ) && is_array( $t->args ) && $args != array_merge( $args, $t->args ) ) {
     1853                                unset( $taxonomies[ $index ] );
     1854                                $terms = array_merge( $terms, wp_get_object_terms( $object_ids, $taxonomy, array_merge( $args, $t->args ) ) );
     1855                        }
     1856                }
     1857        } else {
     1858                $t = get_taxonomy( $taxonomies[0] );
     1859                if ( isset( $t->args ) && is_array( $t->args ) ) {
     1860                        $args = array_merge( $args, $t->args );
     1861                }
     1862        }
     1863
    18441864        $args['taxonomy'] = $taxonomies;
    18451865        $args['object_ids'] = $object_ids;
    18461866
    1847         $terms = get_terms( $args );
     1867        $terms = array_merge( $terms, get_terms( $args ) );
    18481868
    18491869        /**
    18501870         * Filters the terms for a given object or objects.
  • src/wp-includes/user.php

    diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php
    index dcd1026..2bb4e05 100644
    a b function count_users($strategy = 'time') { 
    857857                $select_count = implode(', ', $select_count);
    858858
    859859                // Add the meta_value index to the selection list, then run the query.
    860                 $row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
     860                $row = $wpdb->get_row( "
     861                        SELECT {$select_count}, COUNT(*)
     862                        FROM {$wpdb->usermeta}
     863                        INNER JOIN {$wpdb->users} ON user_id = ID
     864                        WHERE meta_key = '{$blog_prefix}capabilities'
     865                ", ARRAY_N );
    861866
    862867                // Run the previous loop again to associate results with role names.
    863868                $col = 0;
    function count_users($strategy = 'time') { 
    881886                        'none' => 0,
    882887                );
    883888
    884                 $users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
     889                $users_of_blog = $wpdb->get_col( "
     890                        SELECT meta_value
     891                        FROM {$wpdb->usermeta}
     892                        INNER JOIN {$wpdb->users} ON user_id = ID
     893                        WHERE meta_key = '{$blog_prefix}capabilities'
     894                " );
    885895
    886896                foreach ( $users_of_blog as $caps_meta ) {
    887897                        $b_roles = maybe_unserialize($caps_meta);
  • tests/phpunit/includes/bootstrap.php

    diff --git a/tests/phpunit/includes/bootstrap.php b/tests/phpunit/includes/bootstrap.php
    index b462e70..1450736 100644
    a b  
    33 * Installs WordPress for running the tests and loads WordPress and the test libraries
    44 */
    55
     6/**
     7 * Compatibility with PHPUnit 6+
     8 */
     9if ( class_exists( 'PHPUnit\Runner\Version' ) ) {
     10        require_once dirname( __FILE__ ) . '/phpunit6-compat.php';
     11}
    612
    713$config_file_path = dirname( dirname( __FILE__ ) );
    814if ( ! file_exists( $config_file_path . '/wp-tests-config.php' ) ) {
  • new file tests/phpunit/includes/object-cache.php

    diff --git a/tests/phpunit/includes/object-cache.php b/tests/phpunit/includes/object-cache.php
    new file mode 100644
    index 0000000..51f3e91
    - +  
     1<?php
     2/**
     3 * Adds a value to cache.
     4 *
     5 * If the specified key already exists, the value is not stored and the function
     6 * returns false.
     7 *
     8 * @link http://www.php.net/manual/en/memcached.add.php
     9 *
     10 * @param string    $key        The key under which to store the value.
     11 * @param mixed     $value      The value to store.
     12 * @param string    $group      The group value appended to the $key.
     13 * @param int       $expiration The expiration time, defaults to 0.
     14 * @return bool                 Returns TRUE on success or FALSE on failure.
     15 */
     16function wp_cache_add( $key, $value, $group = '', $expiration = 0 ) {
     17        global $wp_object_cache;
     18        return $wp_object_cache->add( $key, $value, $group, $expiration );
     19}
     20
     21/**
     22 * Adds a value to cache on a specific server.
     23 *
     24 * Using a server_key value, the object can be stored on a specified server as opposed
     25 * to a random server in the stack. Note that this method will add the key/value to the
     26 * _cache object as part of the runtime cache. It will add it to an array for the
     27 * specified server_key.
     28 *
     29 * @link http://www.php.net/manual/en/memcached.addbykey.php
     30 *
     31 * @param string    $server_key     The key identifying the server to store the value on.
     32 * @param string    $key            The key under which to store the value.
     33 * @param mixed     $value          The value to store.
     34 * @param string    $group          The group value appended to the $key.
     35 * @param int       $expiration     The expiration time, defaults to 0.
     36 * @return bool                     Returns TRUE on success or FALSE on failure.
     37 */
     38function wp_cache_add_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
     39        global $wp_object_cache;
     40        return $wp_object_cache->addByKey( $server_key, $key, $value, $group, $expiration );
     41}
     42
     43/**
     44 * Add a single server to the list of Memcached servers.
     45 *
     46 * @link http://www.php.net/manual/en/memcached.addserver.php
     47 *
     48 * @param string        $host   The hostname of the memcache server.
     49 * @param int           $port   The port on which memcache is running.
     50 * @param int           $weight The weight of the server relative to the total weight of all the servers in the pool.
     51 * @return bool                 Returns TRUE on success or FALSE on failure.
     52 */
     53function wp_cache_add_server( $host, $port, $weight = 0 ) {
     54        global $wp_object_cache;
     55        return $wp_object_cache->addServer( $host, $port, $weight );
     56}
     57
     58/**
     59 * Adds an array of servers to the pool.
     60 *
     61 * Each individual server in the array must include a domain and port, with an optional
     62 * weight value: $servers = array( array( '127.0.0.1', 11211, 0 ) );
     63 *
     64 * @link http://www.php.net/manual/en/memcached.addservers.php
     65 *
     66 * @param array     $servers    Array of server to register.
     67 * @return bool                 True on success; false on failure.
     68 */
     69function wp_cache_add_servers( $servers ) {
     70        global $wp_object_cache;
     71        return $wp_object_cache->addServers( $servers );
     72}
     73
     74/**
     75 * Append data to an existing item.
     76 *
     77 * This method should throw an error if it is used with compressed data. This
     78 * is an expected behavior. Memcached casts the value to be appended to the initial value to the
     79 * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
     80 * how memcached treats types, the behavior has been mimicked in the internal cache to produce
     81 * similar results and improve consistency. It is recommend that appends only occur with data of
     82 * the same type.
     83 *
     84 * @link http://www.php.net/manual/en/memcached.append.php
     85 *
     86 * @param string    $key    The key under which to store the value.
     87 * @param mixed     $value  Must be string as appending mixed values is not well-defined
     88 * @param string    $group  The group value appended to the $key.
     89 * @return bool             Returns TRUE on success or FALSE on failure.
     90 */
     91function wp_cache_append( $key, $value, $group = '' ) {
     92        global $wp_object_cache;
     93        return $wp_object_cache->append( $key, $value, $group );
     94}
     95
     96/**
     97 * Append data to an existing item by server key.
     98 *
     99 * This method should throw an error if it is used with compressed data. This
     100 * is an expected behavior. Memcached casts the value to be appended to the initial value to the
     101 * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
     102 * how memcached treats types, the behavior has been mimicked in the internal cache to produce
     103 * similar results and improve consistency. It is recommend that appends only occur with data of
     104 * the same type.
     105 *
     106 * @link http://www.php.net/manual/en/memcached.appendbykey.php
     107 *
     108 * @param string    $server_key     The key identifying the server to store the value on.
     109 * @param string    $key            The key under which to store the value.
     110 * @param mixed     $value          Must be string as appending mixed values is not well-defined
     111 * @param string    $group          The group value appended to the $key.
     112 * @return bool                     Returns TRUE on success or FALSE on failure.
     113 */
     114function wp_cache_append_by_key( $server_key, $key, $value, $group = '' ) {
     115        global $wp_object_cache;
     116        return $wp_object_cache->appendByKey( $server_key, $key, $value, $group );
     117}
     118
     119/**
     120 * Performs a "check and set" to store data.
     121 *
     122 * The set will be successful only if the no other request has updated the value since it was fetched by
     123 * this request.
     124 *
     125 * @link http://www.php.net/manual/en/memcached.cas.php
     126 *
     127 * @param float     $cas_token  Unique value associated with the existing item. Generated by memcached.
     128 * @param string    $key        The key under which to store the value.
     129 * @param mixed     $value      The value to store.
     130 * @param string    $group      The group value appended to the $key.
     131 * @param int       $expiration The expiration time, defaults to 0.
     132 * @return bool                 Returns TRUE on success or FALSE on failure.
     133 */
     134function wp_cache_cas( $cas_token, $key, $value, $group = '', $expiration = 0 ) {
     135        global $wp_object_cache;
     136        return $wp_object_cache->cas( $cas_token, $key, $value, $group, $expiration );
     137}
     138
     139/**
     140 * Performs a "check and set" to store data with a server key.
     141 *
     142 * The set will be successful only if the no other request has updated the value since it was fetched by
     143 * this request.
     144 *
     145 * @link http://www.php.net/manual/en/memcached.casbykey.php
     146 *
     147 * @param string    $server_key The key identifying the server to store the value on.
     148 * @param float     $cas_token  Unique value associated with the existing item. Generated by memcached.
     149 * @param string    $key        The key under which to store the value.
     150 * @param mixed     $value      The value to store.
     151 * @param string    $group      The group value appended to the $key.
     152 * @param int       $expiration The expiration time, defaults to 0.
     153 * @return bool                 Returns TRUE on success or FALSE on failure.
     154 */
     155function wp_cache_cas_by_key( $cas_token, $server_key, $key, $value, $group = '', $expiration = 0 ) {
     156        global $wp_object_cache;
     157        return $wp_object_cache->casByKey( $cas_token, $server_key, $key, $value, $group, $expiration );
     158}
     159
     160/**
     161 * Closes the cache.
     162 *
     163 * This function has ceased to do anything since WordPress 2.5. The
     164 * functionality was removed along with the rest of the persistent cache. This
     165 * does not mean that plugins can't implement this function when they need to
     166 * make sure that the cache is cleaned up after WordPress no longer needs it.
     167 *
     168 * @since 2.0.0
     169 *
     170 * @return  bool    Always returns True
     171 */
     172function wp_cache_close() {
     173        return true;
     174}
     175
     176/**
     177 * Decrement a numeric item's value.
     178 *
     179 * @link http://www.php.net/manual/en/memcached.decrement.php
     180 *
     181 * @param string    $key    The key under which to store the value.
     182 * @param int       $offset The amount by which to decrement the item's value.
     183 * @param string    $group  The group value appended to the $key.
     184 * @return int|bool         Returns item's new value on success or FALSE on failure.
     185 */
     186function wp_cache_decrement( $key, $offset = 1, $group = '' ) {
     187        global $wp_object_cache;
     188        return $wp_object_cache->decrement( $key, $offset, $group );
     189}
     190
     191/**
     192 * Decrement a numeric item's value.
     193 *
     194 * Same as wp_cache_decrement. Original WordPress caching backends use wp_cache_decr. I
     195 * want both spellings to work.
     196 *
     197 * @link http://www.php.net/manual/en/memcached.decrement.php
     198 *
     199 * @param string    $key    The key under which to store the value.
     200 * @param int       $offset The amount by which to decrement the item's value.
     201 * @param string    $group  The group value appended to the $key.
     202 * @return int|bool         Returns item's new value on success or FALSE on failure.
     203 */
     204function wp_cache_decr( $key, $offset = 1, $group = '' ) {
     205        return wp_cache_decrement( $key, $offset, $group );
     206}
     207
     208/**
     209 * Remove the item from the cache.
     210 *
     211 * Remove an item from memcached with identified by $key after $time seconds. The
     212 * $time parameter allows an object to be queued for deletion without immediately
     213 * deleting. Between the time that it is queued and the time it's deleted, add,
     214 * replace, and get will fail, but set will succeed.
     215 *
     216 * @link http://www.php.net/manual/en/memcached.delete.php
     217 *
     218 * @param string    $key    The key under which to store the value.
     219 * @param string    $group  The group value appended to the $key.
     220 * @param int       $time   The amount of time the server will wait to delete the item in seconds.
     221 * @return bool             Returns TRUE on success or FALSE on failure.
     222 */
     223function wp_cache_delete( $key, $group = '', $time = 0 ) {
     224        global $wp_object_cache;
     225        return $wp_object_cache->delete( $key, $group, $time );
     226}
     227
     228/**
     229 * Remove the item from the cache by server key.
     230 *
     231 * Remove an item from memcached with identified by $key after $time seconds. The
     232 * $time parameter allows an object to be queued for deletion without immediately
     233 * deleting. Between the time that it is queued and the time it's deleted, add,
     234 * replace, and get will fail, but set will succeed.
     235 *
     236 * @link http://www.php.net/manual/en/memcached.deletebykey.php
     237 *
     238 * @param string        $server_key The key identifying the server to store the value on.
     239 * @param string        $key        The key under which to store the value.
     240 * @param string        $group      The group value appended to the $key.
     241 * @param int           $time       The amount of time the server will wait to delete the item in seconds.
     242 * @return bool                     Returns TRUE on success or FALSE on failure.
     243 */
     244function wp_cache_delete_by_key( $server_key, $key, $group = '', $time = 0 ) {
     245        global $wp_object_cache;
     246        return $wp_object_cache->deleteByKey( $server_key, $key, $group, $time );
     247}
     248
     249/**
     250 * Fetch the next result.
     251 *
     252 * @link http://www.php.net/manual/en/memcached.fetch.php
     253 *
     254 * @return  array|bool   Returns the next result or FALSE otherwise.
     255 */
     256function wp_cache_fetch() {
     257        global $wp_object_cache;
     258        return $wp_object_cache->fetch();
     259}
     260
     261/**
     262 * Fetch all remaining results from the last request.
     263 *
     264 * @link http://www.php.net/manual/en/memcached.fetchall.php
     265 *
     266 * @return  array|bool  Returns the results or FALSE on failure.
     267 */
     268function wp_cache_fetch_all() {
     269        global $wp_object_cache;
     270        return $wp_object_cache->fetchAll();
     271}
     272
     273/**
     274 * Invalidate all items in the cache.
     275 *
     276 * @link http://www.php.net/manual/en/memcached.flush.php
     277 *
     278 * @param int       $delay  Number of seconds to wait before invalidating the items.
     279 * @return bool             Returns TRUE on success or FALSE on failure.
     280 */
     281function wp_cache_flush( $delay = 0 ) {
     282        global $wp_object_cache;
     283        return $wp_object_cache->flush( $delay );
     284}
     285
     286/**
     287 * Retrieve object from cache.
     288 *
     289 * Gets an object from cache based on $key and $group. In order to fully support the $cache_cb and $cas_token
     290 * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
     291 * those values are set, the request is made directly to the memcached server for proper handling of the
     292 * callback and/or token.
     293 *
     294 * Note that the $deprecated and $found args are only here for compatibility with the native wp_cache_get function.
     295 *
     296 * @link http://www.php.net/manual/en/memcached.get.php
     297 *
     298 * @param string        $key        The key under which to store the value.
     299 * @param string        $group      The group value appended to the $key.
     300 * @param bool          $force      Whether or not to force a cache invalidation.
     301 * @param null|bool     $found      Variable passed by reference to determine if the value was found or not.
     302 * @param null|string   $cache_cb   Read-through caching callback.
     303 * @param null|float    $cas_token  The variable to store the CAS token in.
     304 * @return bool|mixed               Cached object value.
     305 */
     306function wp_cache_get( $key, $group = '', $force = false, &$found = null, $cache_cb = null, &$cas_token = null ) {
     307        global $wp_object_cache;
     308
     309        if ( func_num_args() > 4 )
     310                return $wp_object_cache->get( $key, $group, $force, $found, '', false, $cache_cb, $cas_token );
     311        else
     312                return $wp_object_cache->get( $key, $group, $force, $found );
     313}
     314
     315/**
     316 * Retrieve object from cache from specified server.
     317 *
     318 * Gets an object from cache based on $key, $group and $server_key. In order to fully support the $cache_cb and $cas_token
     319 * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
     320 * those values are set, the request is made directly to the memcached server for proper handling of the
     321 * callback and/or token.
     322 *
     323 * @link http://www.php.net/manual/en/memcached.getbykey.php
     324 *
     325 * @param string        $server_key The key identifying the server to store the value on.
     326 * @param string        $key        The key under which to store the value.
     327 * @param string        $group      The group value appended to the $key.
     328 * @param bool          $force      Whether or not to force a cache invalidation.
     329 * @param null|bool     $found      Variable passed by reference to determine if the value was found or not.
     330 * @param null|string   $cache_cb   Read-through caching callback.
     331 * @param null|float    $cas_token  The variable to store the CAS token in.
     332 * @return bool|mixed               Cached object value.
     333 */
     334function wp_cache_get_by_key( $server_key, $key, $group = '', $force = false, &$found = null, $cache_cb = NULL, &$cas_token = NULL ) {
     335        global $wp_object_cache;
     336
     337        if ( func_num_args() > 5 )
     338                return $wp_object_cache->getByKey( $server_key, $key, $group, $force, $found, $cache_cb, $cas_token );
     339        else
     340                return $wp_object_cache->getByKey( $server_key, $key, $group, $force, $found );
     341}
     342
     343/**
     344 * Request multiple keys without blocking.
     345 *
     346 * @link http://www.php.net/manual/en/memcached.getdelayed.php
     347 *
     348 * @param string|array  $keys       Array or string of key(s) to request.
     349 * @param string|array  $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
     350 * @param bool          $with_cas   Whether to request CAS token values also.
     351 * @param null          $value_cb   The result callback or NULL.
     352 * @return bool                     Returns TRUE on success or FALSE on failure.
     353 */
     354function wp_cache_get_delayed( $keys, $groups = '', $with_cas = false, $value_cb = NULL ) {
     355        global $wp_object_cache;
     356        return $wp_object_cache->getDelayed( $keys, $groups, $with_cas, $value_cb );
     357}
     358
     359/**
     360 * Request multiple keys without blocking from a specified server.
     361 *
     362 * @link http://www.php.net/manual/en/memcached.getdelayed.php
     363 *
     364 * @param string        $server_key The key identifying the server to store the value on.
     365 * @param string|array  $keys       Array or string of key(s) to request.
     366 * @param string|array  $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
     367 * @param bool          $with_cas   Whether to request CAS token values also.
     368 * @param null          $value_cb   The result callback or NULL.
     369 * @return bool                     Returns TRUE on success or FALSE on failure.
     370 */
     371function wp_cache_get_delayed_by_key( $server_key, $keys, $groups = '', $with_cas = false, $value_cb = NULL ) {
     372        global $wp_object_cache;
     373        return $wp_object_cache->getDelayedByKey( $server_key, $keys, $groups, $with_cas, $value_cb );
     374}
     375
     376/**
     377 * Gets multiple values from memcached in one request.
     378 *
     379 * See the buildKeys method definition to understand the $keys/$groups parameters.
     380 *
     381 * @link http://www.php.net/manual/en/memcached.getmulti.php
     382 *
     383 * @param array         $keys       Array of keys to retrieve.
     384 * @param string|array  $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
     385 * @param null|array    $cas_tokens The variable to store the CAS tokens for the found items.
     386 * @param int           $flags      The flags for the get operation.
     387 * @return bool|array               Returns the array of found items or FALSE on failure.
     388 */
     389function wp_cache_get_multi( $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) {
     390        global $wp_object_cache;
     391
     392        if ( func_num_args() > 2 )
     393                return $wp_object_cache->getMulti( $keys, $groups, '', $cas_tokens, $flags );
     394        else
     395                return $wp_object_cache->getMulti( $keys, $groups );
     396}
     397
     398/**
     399 * Gets multiple values from memcached in one request by specified server key.
     400 *
     401 * See the buildKeys method definition to understand the $keys/$groups parameters.
     402 *
     403 * @link http://www.php.net/manual/en/memcached.getmultibykey.php
     404 *
     405 * @param string        $server_key The key identifying the server to store the value on.
     406 * @param array         $keys       Array of keys to retrieve.
     407 * @param string|array  $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
     408 * @param null|array    $cas_tokens The variable to store the CAS tokens for the found items.
     409 * @param int           $flags      The flags for the get operation.
     410 * @return bool|array               Returns the array of found items or FALSE on failure.
     411 */
     412function wp_cache_get_multi_by_key( $server_key, $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) {
     413        global $wp_object_cache;
     414
     415        if ( func_num_args() > 3 )
     416                return $wp_object_cache->getMultiByKey( $server_key, $keys, $groups, $cas_tokens, $flags );
     417        else
     418                return $wp_object_cache->getMultiByKey( $server_key, $keys, $groups );
     419}
     420
     421/**
     422 * Retrieve a Memcached option value.
     423 *
     424 * @link http://www.php.net/manual/en/memcached.getoption.php
     425 *
     426 * @param int   $option One of the Memcached::OPT_* constants.
     427 * @return mixed        Returns the value of the requested option, or FALSE on error.
     428 */
     429function wp_cache_get_option( $option ) {
     430        global $wp_object_cache;
     431        return $wp_object_cache->getOption( $option );
     432}
     433
     434/**
     435 * Return the result code of the last option.
     436 *
     437 * @link http://www.php.net/manual/en/memcached.getresultcode.php
     438 *
     439 * @return int  Result code of the last Memcached operation.
     440 */
     441function wp_cache_get_result_code() {
     442        global $wp_object_cache;
     443        return $wp_object_cache->getResultCode();
     444}
     445
     446/**
     447 * Return the message describing the result of the last operation.
     448 *
     449 * @link http://www.php.net/manual/en/memcached.getresultmessage.php
     450 *
     451 * @return string   Message describing the result of the last Memcached operation.
     452 */
     453function wp_cache_get_result_message() {
     454        global $wp_object_cache;
     455        return $wp_object_cache->getResultMessage();
     456}
     457
     458/**
     459 * Get server information by key.
     460 *
     461 * @link http://www.php.net/manual/en/memcached.getserverbykey.php
     462 *
     463 * @param string    $server_key The key identifying the server to store the value on.
     464 * @return array                Array with host, post, and weight on success, FALSE on failure.
     465 */
     466function wp_cache_get_server_by_key( $server_key ) {
     467        global $wp_object_cache;
     468        return $wp_object_cache->getServerByKey( $server_key );
     469}
     470
     471/**
     472 * Get the list of servers in the pool.
     473 *
     474 * @link http://www.php.net/manual/en/memcached.getserverlist.php
     475 *
     476 * @return array    The list of all servers in the server pool.
     477 */
     478function wp_cache_get_server_list() {
     479        global $wp_object_cache;
     480        return $wp_object_cache->getServerList();
     481}
     482
     483/**
     484 * Get server pool statistics.
     485 *
     486 * @link http://www.php.net/manual/en/memcached.getstats.php
     487 *
     488 * @return array    Array of server statistics, one entry per server.
     489 */
     490function wp_cache_get_stats() {
     491        global $wp_object_cache;
     492        return $wp_object_cache->getStats();
     493}
     494
     495/**
     496 * Get server pool memcached version information.
     497 *
     498 * @link http://www.php.net/manual/en/memcached.getversion.php
     499 *
     500 * @return array    Array of server versions, one entry per server.
     501 */
     502function wp_cache_get_version() {
     503        global $wp_object_cache;
     504        return $wp_object_cache->getVersion();
     505}
     506
     507/**
     508 * Increment a numeric item's value.
     509 *
     510 * @link http://www.php.net/manual/en/memcached.increment.php
     511 *
     512 * @param string    $key    The key under which to store the value.
     513 * @param int       $offset The amount by which to increment the item's value.
     514 * @param string    $group  The group value appended to the $key.
     515 * @return int|bool         Returns item's new value on success or FALSE on failure.
     516 */
     517function wp_cache_increment( $key, $offset = 1, $group = '' ) {
     518        global $wp_object_cache;
     519        return $wp_object_cache->increment( $key, $offset, $group );
     520}
     521
     522/**
     523 * Increment a numeric item's value.
     524 *
     525 * This is the same as wp_cache_increment, but kept for back compatibility. The original
     526 * WordPress caching backends use wp_cache_incr. I want both to work.
     527 *
     528 * @link http://www.php.net/manual/en/memcached.increment.php
     529 *
     530 * @param string    $key    The key under which to store the value.
     531 * @param int       $offset The amount by which to increment the item's value.
     532 * @param string    $group  The group value appended to the $key.
     533 * @return int|bool         Returns item's new value on success or FALSE on failure.
     534 */
     535function wp_cache_incr( $key, $offset = 1, $group = '' ) {
     536        return wp_cache_increment( $key, $offset, $group );
     537}
     538
     539/**
     540 * Prepend data to an existing item.
     541 *
     542 * This method should throw an error if it is used with compressed data. This is an expected behavior.
     543 * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
     544 * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
     545 * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
     546 * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
     547 * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
     548 * that prepends only occur with data of the same type.
     549 *
     550 * @link http://www.php.net/manual/en/memcached.prepend.php
     551 *
     552 * @param string    $key    The key under which to store the value.
     553 * @param string    $value  Must be string as prepending mixed values is not well-defined.
     554 * @param string    $group  The group value prepended to the $key.
     555 * @return bool             Returns TRUE on success or FALSE on failure.
     556 */
     557function wp_cache_prepend( $key, $value, $group = '' ) {
     558        global $wp_object_cache;
     559        return $wp_object_cache->prepend( $key, $value, $group );
     560}
     561
     562/**
     563 * Append data to an existing item by server key.
     564 *
     565 * This method should throw an error if it is used with compressed data. This is an expected behavior.
     566 * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
     567 * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
     568 * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
     569 * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
     570 * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
     571 * that prepends only occur with data of the same type.
     572 *
     573 * @link http://www.php.net/manual/en/memcached.prependbykey.php
     574 *
     575 * @param string    $server_key     The key identifying the server to store the value on.
     576 * @param string    $key            The key under which to store the value.
     577 * @param string    $value          Must be string as prepending mixed values is not well-defined.
     578 * @param string    $group          The group value prepended to the $key.
     579 * @return bool                     Returns TRUE on success or FALSE on failure.
     580 */
     581function wp_cache_prepend_by_key( $server_key, $key, $value, $group = '' ) {
     582        global $wp_object_cache;
     583        return $wp_object_cache->prependByKey( $server_key, $key, $value, $group );
     584}
     585
     586/**
     587 * Replaces a value in cache.
     588 *
     589 * This method is similar to "add"; however, is does not successfully set a value if
     590 * the object's key is not already set in cache.
     591 *
     592 * @link http://www.php.net/manual/en/memcached.replace.php
     593 *
     594 * @param string    $key        The key under which to store the value.
     595 * @param mixed     $value      The value to store.
     596 * @param string    $group      The group value appended to the $key.
     597 * @param int       $expiration The expiration time, defaults to 0.
     598 * @return bool                 Returns TRUE on success or FALSE on failure.
     599 */
     600function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) {
     601        global $wp_object_cache;
     602        return $wp_object_cache->replace( $key, $value, $group, $expiration );
     603}
     604
     605/**
     606 * Replaces a value in cache on a specific server.
     607 *
     608 * This method is similar to "addByKey"; however, is does not successfully set a value if
     609 * the object's key is not already set in cache.
     610 *
     611 * @link http://www.php.net/manual/en/memcached.addbykey.php
     612 *
     613 * @param string    $server_key     The key identifying the server to store the value on.
     614 * @param string    $key            The key under which to store the value.
     615 * @param mixed     $value          The value to store.
     616 * @param string    $group          The group value appended to the $key.
     617 * @param int       $expiration     The expiration time, defaults to 0.
     618 * @return bool                     Returns TRUE on success or FALSE on failure.
     619 */
     620function wp_cache_replace_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
     621        global $wp_object_cache;
     622        return $wp_object_cache->replaceByKey( $server_key, $key, $value, $group, $expiration );
     623}
     624
     625/**
     626 * Sets a value in cache.
     627 *
     628 * The value is set whether or not this key already exists in memcached.
     629 *
     630 * @link http://www.php.net/manual/en/memcached.set.php
     631 *
     632 * @param string    $key        The key under which to store the value.
     633 * @param mixed     $value      The value to store.
     634 * @param string    $group      The group value appended to the $key.
     635 * @param int       $expiration The expiration time, defaults to 0.
     636 * @return bool                 Returns TRUE on success or FALSE on failure.
     637 */
     638function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) {
     639        global $wp_object_cache;
     640        return $wp_object_cache->set( $key, $value, $group, $expiration );
     641}
     642
     643/**
     644 * Sets a value in cache.
     645 *
     646 * The value is set whether or not this key already exists in memcached.
     647 *
     648 * @link http://www.php.net/manual/en/memcached.set.php
     649 *
     650 * @param string    $server_key     The key identifying the server to store the value on.
     651 * @param string    $key            The key under which to store the value.
     652 * @param mixed     $value          The value to store.
     653 * @param string    $group          The group value appended to the $key.
     654 * @param int       $expiration     The expiration time, defaults to 0.
     655 * @return bool                     Returns TRUE on success or FALSE on failure.
     656 */
     657function wp_cache_set_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
     658        global $wp_object_cache;
     659        return $wp_object_cache->setByKey( $server_key, $key, $value, $group, $expiration );
     660}
     661
     662/**
     663 * Set multiple values to cache at once.
     664 *
     665 * By sending an array of $items to this function, all values are saved at once to
     666 * memcached, reducing the need for multiple requests to memcached. The $items array
     667 * keys and values are what are stored to memcached. The keys in the $items array
     668 * are merged with the $groups array/string value via buildKeys to determine the
     669 * final key for the object.
     670 *
     671 * @param array         $items      An array of key/value pairs to store on the server.
     672 * @param string|array  $groups     Group(s) to merge with key(s) in $items.
     673 * @param int           $expiration The expiration time, defaults to 0.
     674 * @return bool                     Returns TRUE on success or FALSE on failure.
     675 */
     676function wp_cache_set_multi( $items, $groups = '', $expiration = 0 ) {
     677        global $wp_object_cache;
     678        return $wp_object_cache->setMulti( $items, $groups, $expiration );
     679}
     680
     681/**
     682 * Set multiple values to cache at once on specified server.
     683 *
     684 * By sending an array of $items to this function, all values are saved at once to
     685 * memcached, reducing the need for multiple requests to memcached. The $items array
     686 * keys and values are what are stored to memcached. The keys in the $items array
     687 * are merged with the $groups array/string value via buildKeys to determine the
     688 * final key for the object.
     689 *
     690 * @param string        $server_key The key identifying the server to store the value on.
     691 * @param array         $items      An array of key/value pairs to store on the server.
     692 * @param string|array  $groups     Group(s) to merge with key(s) in $items.
     693 * @param int           $expiration The expiration time, defaults to 0.
     694 * @return bool                     Returns TRUE on success or FALSE on failure.
     695 */
     696function wp_cache_set_multi_by_key( $server_key, $items, $groups = 'default', $expiration = 0 ) {
     697        global $wp_object_cache;
     698        return $wp_object_cache->setMultiByKey( $server_key, $items, $groups, $expiration );
     699}
     700
     701/**
     702 * Set a Memcached option.
     703 *
     704 * @link http://www.php.net/manual/en/memcached.setoption.php
     705 *
     706 * @param int       $option Option name.
     707 * @param mixed     $value  Option value.
     708 * @return bool             Returns TRUE on success or FALSE on failure.
     709 */
     710function wp_cache_set_option( $option, $value ) {
     711        global $wp_object_cache;
     712        return $wp_object_cache->setOption( $option, $value );
     713}
     714
     715/**
     716 * Switch blog prefix, which changes the cache that is accessed.
     717 *
     718 * @param  int     $blog_id    Blog to switch to.
     719 * @return void
     720 */
     721function wp_cache_switch_to_blog( $blog_id ) {
     722        global $wp_object_cache;
     723        return $wp_object_cache->switch_to_blog( $blog_id );
     724}
     725
     726
     727/**
     728 * Sets up Object Cache Global and assigns it.
     729 *
     730 * @global  WP_Object_Cache     $wp_object_cache    WordPress Object Cache
     731 * @return  void
     732 */
     733function wp_cache_init() {
     734        global $wp_object_cache;
     735        $wp_object_cache = new WP_Object_Cache();
     736}
     737
     738/**
     739 * Adds a group or set of groups to the list of non-persistent groups.
     740 *
     741 * @param   string|array    $groups     A group or an array of groups to add.
     742 * @return  void
     743 */
     744function wp_cache_add_global_groups( $groups ) {
     745        global $wp_object_cache;
     746        $wp_object_cache->add_global_groups( $groups );
     747}
     748
     749/**
     750 * Adds a group or set of groups to the list of non-Memcached groups.
     751 *
     752 * @param   string|array    $groups     A group or an array of groups to add.
     753 * @return  void
     754 */
     755function wp_cache_add_non_persistent_groups( $groups ) {
     756        global $wp_object_cache;
     757        $wp_object_cache->add_non_persistent_groups( $groups );
     758}
     759
     760class WP_Object_Cache {
     761
     762        /**
     763         * Holds the Memcached object.
     764         *
     765         * @var Memcached
     766         */
     767        public $m;
     768
     769        /**
     770         * Hold the Memcached server details.
     771         *
     772         * @var array
     773         */
     774        public $servers;
     775
     776        /**
     777         * Holds the non-Memcached objects.
     778         *
     779         * @var array
     780         */
     781        public $cache = array();
     782
     783        /**
     784         * List of global groups.
     785         *
     786         * @var array
     787         */
     788        public $global_groups = array( 'users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss' );
     789
     790        /**
     791         * List of groups not saved to Memcached.
     792         *
     793         * @var array
     794         */
     795        public $no_mc_groups = array( 'comment', 'counts' );
     796
     797        /**
     798         * Prefix used for global groups.
     799         *
     800         * @var string
     801         */
     802        public $global_prefix = '';
     803
     804        /**
     805         * Prefix used for non-global groups.
     806         *
     807         * @var string
     808         */
     809        public $blog_prefix = '';
     810
     811        /**
     812         * Instantiate the Memcached class.
     813         *
     814         * Instantiates the Memcached class and returns adds the servers specified
     815         * in the $memcached_servers global array.
     816         *
     817         * @link    http://www.php.net/manual/en/memcached.construct.php
     818         *
     819         * @param   null    $persistent_id      To create an instance that persists between requests, use persistent_id to specify a unique ID for the instance.
     820         */
     821        public function __construct( $persistent_id = NULL ) {
     822                global $memcached_servers, $blog_id, $table_prefix;
     823
     824                if ( is_null( $persistent_id ) || ! is_string( $persistent_id ) )
     825                        $this->m = new Memcached();
     826                else
     827                        $this->m = new Memcached( $persistent_id );
     828
     829                if ( isset( $memcached_servers ) )
     830                        $this->servers = $memcached_servers;
     831                else
     832                        $this->servers = array( array( '127.0.0.1', 11211 ) );
     833
     834                $this->addServers( $this->servers );
     835
     836                /**
     837                 * This approach is borrowed from Sivel and Boren. Use the salt for easy cache invalidation and for
     838                 * multi single WP installs on the same server.
     839                 */
     840                if ( ! defined( 'WP_CACHE_KEY_SALT' ) )
     841                        define( 'WP_CACHE_KEY_SALT', '' );
     842
     843                // Assign global and blog prefixes for use with keys
     844                if ( function_exists( 'is_multisite' ) ) {
     845                        $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
     846                        $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
     847                }
     848
     849                // Setup cacheable values for handling expiration times
     850                $this->thirty_days = 60 * 60 * 24 * 30;
     851                $this->now         = time();
     852        }
     853
     854        /**
     855         * Adds a value to cache.
     856         *
     857         * If the specified key already exists, the value is not stored and the function
     858         * returns false.
     859         *
     860         * @link    http://www.php.net/manual/en/memcached.add.php
     861         *
     862         * @param   string      $key            The key under which to store the value.
     863         * @param   mixed       $value          The value to store.
     864         * @param   string      $group          The group value appended to the $key.
     865         * @param   int         $expiration     The expiration time, defaults to 0.
     866         * @param   string      $server_key     The key identifying the server to store the value on.
     867         * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
     868         * @return  bool                        Returns TRUE on success or FALSE on failure.
     869         */
     870        public function add( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
     871                /*
     872                 * Ensuring that wp_suspend_cache_addition is defined before calling, because sometimes an advanced-cache.php
     873                 * file will load object-cache.php before wp-includes/functions.php is loaded. In those cases, if wp_cache_add
     874                 * is called in advanced-cache.php before any more of WordPress is loaded, we get a fatal error because
     875                 * wp_suspend_cache_addition will not be defined until wp-includes/functions.php is loaded.
     876                 */
     877                if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) {
     878                        return false;
     879                }
     880
     881                $derived_key = $this->buildKey( $key, $group );
     882                $expiration  = $this->sanitize_expiration( $expiration );
     883
     884                // If group is a non-Memcached group, save to runtime cache, not Memcached
     885                if ( in_array( $group, $this->no_mc_groups ) ) {
     886
     887                        // Add does not set the value if the key exists; mimic that here
     888                        if ( isset( $this->cache[$derived_key] ) )
     889                                return false;
     890
     891                        $this->add_to_internal_cache( $derived_key, $value );
     892
     893                        return true;
     894                }
     895
     896                // Save to Memcached
     897                if ( $byKey )
     898                        $result = $this->m->addByKey( $server_key, $derived_key, $value, $expiration );
     899                else
     900                        $result = $this->m->add( $derived_key, $value, $expiration );
     901
     902                // Store in runtime cache if add was successful
     903                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     904                        $this->add_to_internal_cache( $derived_key, $value );
     905
     906                return $result;
     907        }
     908
     909        /**
     910         * Adds a value to cache on a specific server.
     911         *
     912         * Using a server_key value, the object can be stored on a specified server as opposed
     913         * to a random server in the stack. Note that this method will add the key/value to the
     914         * _cache object as part of the runtime cache. It will add it to an array for the
     915         * specified server_key.
     916         *
     917         * @link    http://www.php.net/manual/en/memcached.addbykey.php
     918         *
     919         * @param   string      $server_key     The key identifying the server to store the value on.
     920         * @param   string      $key            The key under which to store the value.
     921         * @param   mixed       $value          The value to store.
     922         * @param   string      $group          The group value appended to the $key.
     923         * @param   int         $expiration     The expiration time, defaults to 0.
     924         * @return  bool                        Returns TRUE on success or FALSE on failure.
     925         */
     926        public function addByKey( $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
     927                return $this->add( $key, $value, $group, $expiration, $server_key, true );
     928        }
     929
     930        /**
     931         * Add a single server to the list of Memcached servers.
     932         *
     933         * @link http://www.php.net/manual/en/memcached.addserver.php
     934         *
     935         * @param   string      $host           The hostname of the memcache server.
     936         * @param   int         $port           The port on which memcache is running.
     937         * @param   int         $weight         The weight of the server relative to the total weight of all the servers in the pool.
     938         * @return  bool                        Returns TRUE on success or FALSE on failure.
     939         */
     940        public function addServer( $host, $port, $weight = 0 ) {
     941                $host = is_string( $host ) ? $host : '127.0.0.1';
     942                $port = is_numeric( $port ) && $port > 0 ? $port : 11211;
     943                $weight = is_numeric( $weight ) && $weight > 0 ? $weight : 1;
     944
     945                return $this->m->addServer( $host, $port, $weight );
     946        }
     947
     948        /**
     949         * Adds an array of servers to the pool.
     950         *
     951         * Each individual server in the array must include a domain and port, with an optional
     952         * weight value: $servers = array( array( '127.0.0.1', 11211, 0 ) );
     953         *
     954         * @link    http://www.php.net/manual/en/memcached.addservers.php
     955         *
     956         * @param   array       $servers        Array of server to register.
     957         * @return  bool                        True on success; false on failure.
     958         */
     959        public function addServers( $servers ) {
     960                if ( ! is_object( $this->m ) )
     961                        return false;
     962
     963                return $this->m->addServers( $servers );
     964        }
     965
     966        /**
     967         * Append data to an existing item.
     968         *
     969         * This method should throw an error if it is used with compressed data. This
     970         * is an expected behavior. Memcached casts the value to be appended to the initial value to the
     971         * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
     972         * how memcached treats types, the behavior has been mimicked in the internal cache to produce
     973         * similar results and improve consistency. It is recommend that appends only occur with data of
     974         * the same type.
     975         *
     976         * @link    http://www.php.net/manual/en/memcached.append.php
     977         *
     978         * @param   string      $key            The key under which to store the value.
     979         * @param   mixed       $value          Must be string as appending mixed values is not well-defined.
     980         * @param   string      $group          The group value appended to the $key.
     981         * @param   string      $server_key     The key identifying the server to store the value on.
     982         * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
     983         * @return  bool                        Returns TRUE on success or FALSE on failure.
     984         */
     985        public function append( $key, $value, $group = 'default', $server_key = '', $byKey = false ) {
     986                if ( ! is_string( $value ) && ! is_int( $value ) && ! is_float( $value ) )
     987                        return false;
     988
     989                $derived_key = $this->buildKey( $key, $group );
     990
     991                // If group is a non-Memcached group, append to runtime cache value, not Memcached
     992                if ( in_array( $group, $this->no_mc_groups ) ) {
     993                        if ( ! isset( $this->cache[$derived_key] ) )
     994                                return false;
     995
     996                        $combined = $this->combine_values( $this->cache[$derived_key], $value, 'app' );
     997                        $this->add_to_internal_cache( $derived_key, $combined );
     998                        return true;
     999                }
     1000
     1001                // Append to Memcached value
     1002                if ( $byKey )
     1003                        $result = $this->m->appendByKey( $server_key, $derived_key, $value );
     1004                else
     1005                        $result = $this->m->append( $derived_key, $value );
     1006
     1007                // Store in runtime cache if add was successful
     1008                if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
     1009                        $combined = $this->combine_values( $this->cache[$derived_key], $value, 'app' );
     1010                        $this->add_to_internal_cache( $derived_key, $combined );
     1011                }
     1012
     1013                return $result;
     1014        }
     1015
     1016        /**
     1017         * Append data to an existing item by server key.
     1018         *
     1019         * This method should throw an error if it is used with compressed data. This
     1020         * is an expected behavior. Memcached casts the value to be appended to the initial value to the
     1021         * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
     1022         * how memcached treats types, the behavior has been mimicked in the internal cache to produce
     1023         * similar results and improve consistency. It is recommend that appends only occur with data of
     1024         * the same type.
     1025         *
     1026         * @link    http://www.php.net/manual/en/memcached.appendbykey.php
     1027         *
     1028         * @param   string      $server_key     The key identifying the server to store the value on.
     1029         * @param   string      $key            The key under which to store the value.
     1030         * @param   mixed       $value          Must be string as appending mixed values is not well-defined
     1031         * @param   string      $group          The group value appended to the $key.
     1032         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1033         */
     1034        public function appendByKey( $server_key, $key, $value, $group = 'default' ) {
     1035                return $this->append( $key, $value, $group, $server_key, true );
     1036        }
     1037
     1038        /**
     1039         * Performs a "check and set" to store data.
     1040         *
     1041         * The set will be successful only if the no other request has updated the value since it was fetched since
     1042         * this request.
     1043         *
     1044         * @link    http://www.php.net/manual/en/memcached.cas.php
     1045         *
     1046         * @param   float       $cas_token      Unique value associated with the existing item. Generated by memcached.
     1047         * @param   string      $key            The key under which to store the value.
     1048         * @param   mixed       $value          The value to store.
     1049         * @param   string      $group          The group value appended to the $key.
     1050         * @param   int         $expiration     The expiration time, defaults to 0.
     1051         * @param   string      $server_key     The key identifying the server to store the value on.
     1052         * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
     1053         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1054         */
     1055        public function cas( $cas_token, $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
     1056                $derived_key = $this->buildKey( $key, $group );
     1057                $expiration  = $this->sanitize_expiration( $expiration );
     1058
     1059                /**
     1060                 * If group is a non-Memcached group, save to runtime cache, not Memcached. Note
     1061                 * that since check and set cannot be emulated in the run time cache, this value
     1062                 * operation is treated as a normal "add" for no_mc_groups.
     1063                 */
     1064                if ( in_array( $group, $this->no_mc_groups ) ) {
     1065                        $this->add_to_internal_cache( $derived_key, $value );
     1066                        return true;
     1067                }
     1068
     1069                // Save to Memcached
     1070                if ( $byKey )
     1071                        $result = $this->m->casByKey( $cas_token, $server_key, $derived_key, $value, $expiration );
     1072                else
     1073                        $result = $this->m->cas( $cas_token, $derived_key, $value, $expiration );
     1074
     1075                // Store in runtime cache if cas was successful
     1076                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1077                        $this->add_to_internal_cache( $derived_key, $value );
     1078
     1079                return $result;
     1080        }
     1081
     1082        /**
     1083         * Performs a "check and set" to store data with a server key.
     1084         *
     1085         * The set will be successful only if the no other request has updated the value since it was fetched by
     1086         * this request.
     1087         *
     1088         * @link    http://www.php.net/manual/en/memcached.casbykey.php
     1089         *
     1090         * @param   string      $server_key     The key identifying the server to store the value on.
     1091         * @param   float       $cas_token      Unique value associated with the existing item. Generated by memcached.
     1092         * @param   string      $key            The key under which to store the value.
     1093         * @param   mixed       $value          The value to store.
     1094         * @param   string      $group          The group value appended to the $key.
     1095         * @param   int         $expiration     The expiration time, defaults to 0.
     1096         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1097         */
     1098        public function casByKey( $cas_token, $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
     1099                return $this->cas( $cas_token, $key, $value, $group, $expiration, $server_key, true );
     1100        }
     1101
     1102        /**
     1103         * Decrement a numeric item's value.
     1104         *
     1105         * @link http://www.php.net/manual/en/memcached.decrement.php
     1106         *
     1107         * @param string    $key    The key under which to store the value.
     1108         * @param int       $offset The amount by which to decrement the item's value.
     1109         * @param string    $group  The group value appended to the $key.
     1110         * @return int|bool         Returns item's new value on success or FALSE on failure.
     1111         */
     1112        public function decrement( $key, $offset = 1, $group = 'default' ) {
     1113                $derived_key = $this->buildKey( $key, $group );
     1114
     1115                // Decrement values in no_mc_groups
     1116                if ( in_array( $group, $this->no_mc_groups ) ) {
     1117
     1118                        // Only decrement if the key already exists and value is 0 or greater (mimics memcached behavior)
     1119                        if ( isset( $this->cache[$derived_key] ) && $this->cache[$derived_key] >= 0 ) {
     1120
     1121                                // If numeric, subtract; otherwise, consider it 0 and do nothing
     1122                                if ( is_numeric( $this->cache[$derived_key] ) )
     1123                                        $this->cache[$derived_key] -= (int) $offset;
     1124                                else
     1125                                        $this->cache[$derived_key] = 0;
     1126
     1127                                // Returned value cannot be less than 0
     1128                                if ( $this->cache[$derived_key] < 0 )
     1129                                        $this->cache[$derived_key] = 0;
     1130
     1131                                return $this->cache[$derived_key];
     1132                        } else {
     1133                                return false;
     1134                        }
     1135                }
     1136
     1137                $result = $this->m->decrement( $derived_key, $offset );
     1138
     1139                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1140                        $this->add_to_internal_cache( $derived_key, $result );
     1141
     1142                return $result;
     1143        }
     1144
     1145        /**
     1146         * Decrement a numeric item's value.
     1147         *
     1148         * Alias for $this->decrement. Other caching backends use this abbreviated form of the function. It *may* cause
     1149         * breakage somewhere, so it is nice to have. This function will also allow the core unit tests to pass.
     1150         *
     1151         * @param string    $key    The key under which to store the value.
     1152         * @param int       $offset The amount by which to decrement the item's value.
     1153         * @param string    $group  The group value appended to the $key.
     1154         * @return int|bool         Returns item's new value on success or FALSE on failure.
     1155         */
     1156        public function decr( $key, $offset = 1, $group = 'default' ) {
     1157                return $this->decrement( $key, $offset, $group );
     1158        }
     1159
     1160        /**
     1161         * Remove the item from the cache.
     1162         *
     1163         * Remove an item from memcached with identified by $key after $time seconds. The
     1164         * $time parameter allows an object to be queued for deletion without immediately
     1165         * deleting. Between the time that it is queued and the time it's deleted, add,
     1166         * replace, and get will fail, but set will succeed.
     1167         *
     1168         * @link http://www.php.net/manual/en/memcached.delete.php
     1169         *
     1170         * @param   string      $key        The key under which to store the value.
     1171         * @param   string      $group      The group value appended to the $key.
     1172         * @param   int         $time       The amount of time the server will wait to delete the item in seconds.
     1173         * @param   string      $server_key The key identifying the server to store the value on.
     1174         * @param   bool        $byKey      True to store in internal cache by key; false to not store by key
     1175         * @return  bool                    Returns TRUE on success or FALSE on failure.
     1176         */
     1177        public function delete( $key, $group = 'default', $time = 0, $server_key = '', $byKey = false ) {
     1178                $derived_key = $this->buildKey( $key, $group );
     1179
     1180                // Remove from no_mc_groups array
     1181                if ( in_array( $group, $this->no_mc_groups ) ) {
     1182                        if ( isset( $this->cache[$derived_key] ) )
     1183                                unset( $this->cache[$derived_key] );
     1184
     1185                        return true;
     1186                }
     1187
     1188                if ( $byKey )
     1189                        $result = $this->m->deleteByKey( $server_key, $derived_key, $time );
     1190                else
     1191                        $result = $this->m->delete( $derived_key, $time );
     1192
     1193                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1194                        unset( $this->cache[$derived_key] );
     1195
     1196                return $result;
     1197        }
     1198
     1199        /**
     1200         * Remove the item from the cache by server key.
     1201         *
     1202         * Remove an item from memcached with identified by $key after $time seconds. The
     1203         * $time parameter allows an object to be queued for deletion without immediately
     1204         * deleting. Between the time that it is queued and the time it's deleted, add,
     1205         * replace, and get will fail, but set will succeed.
     1206         *
     1207         * @link http://www.php.net/manual/en/memcached.deletebykey.php
     1208         *
     1209         * @param   string      $server_key The key identifying the server to store the value on.
     1210         * @param   string      $key        The key under which to store the value.
     1211         * @param   string      $group      The group value appended to the $key.
     1212         * @param   int         $time       The amount of time the server will wait to delete the item in seconds.
     1213         * @return  bool                    Returns TRUE on success or FALSE on failure.
     1214         */
     1215        public function deleteByKey( $server_key, $key, $group = 'default', $time = 0 ) {
     1216                return $this->delete( $key, $group, $time, $server_key, true );
     1217        }
     1218
     1219        /**
     1220         * Fetch the next result.
     1221         *
     1222         * @link http://www.php.net/manual/en/memcached.fetch.php
     1223         *
     1224         * @return array|bool   Returns the next result or FALSE on failure.
     1225         */
     1226        public function fetch() {
     1227                return $this->m->fetch();
     1228        }
     1229
     1230        /**
     1231         * Fetch all remaining results from the last request.
     1232         *
     1233         * @link http://www.php.net/manual/en/memcached.fetchall.php
     1234         *
     1235         * @return  array|bool          Returns the results or FALSE on failure.
     1236         */
     1237        public function fetchAll() {
     1238                return $this->m->fetchAll();
     1239        }
     1240
     1241        /**
     1242         * Invalidate all items in the cache.
     1243         *
     1244         * @link http://www.php.net/manual/en/memcached.flush.php
     1245         *
     1246         * @param   int     $delay      Number of seconds to wait before invalidating the items.
     1247         * @return  bool                Returns TRUE on success or FALSE on failure.
     1248         */
     1249        public function flush( $delay = 0 ) {
     1250                $result = $this->m->flush( $delay );
     1251
     1252                // Only reset the runtime cache if memcached was properly flushed
     1253                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1254                        $this->cache = array();
     1255
     1256                return $result;
     1257        }
     1258
     1259        /**
     1260         * Retrieve object from cache.
     1261         *
     1262         * Gets an object from cache based on $key and $group. In order to fully support the $cache_cb and $cas_token
     1263         * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
     1264         * those values are set, the request is made directly to the memcached server for proper handling of the
     1265         * callback and/or token. Note that the $cas_token variable cannot be directly passed to the function. The
     1266         * variable need to be first defined with a non null value.
     1267         *
     1268         * If using the $cache_cb argument, the new value will always have an expiration of time of 0 (forever). This
     1269         * is a limitation of the Memcached PECL extension.
     1270         *
     1271         * @link http://www.php.net/manual/en/memcached.get.php
     1272         *
     1273         * @param   string          $key        The key under which to store the value.
     1274         * @param   string          $group      The group value appended to the $key.
     1275         * @param   bool            $force      Whether or not to force a cache invalidation.
     1276         * @param   null|bool       $found      Variable passed by reference to determine if the value was found or not.
     1277         * @param   string          $server_key The key identifying the server to store the value on.
     1278         * @param   bool            $byKey      True to store in internal cache by key; false to not store by key
     1279         * @param   null|callable   $cache_cb   Read-through caching callback.
     1280         * @param   null|float      $cas_token  The variable to store the CAS token in.
     1281         * @return  bool|mixed                  Cached object value.
     1282         */
     1283        public function get( $key, $group = 'default', $force = false, &$found = null, $server_key = '', $byKey = false, $cache_cb = NULL, &$cas_token = NULL ) {
     1284                $derived_key = $this->buildKey( $key, $group );
     1285
     1286                // Assume object is not found
     1287                $found = false;
     1288
     1289                // If either $cache_db, or $cas_token is set, must hit Memcached and bypass runtime cache
     1290                if ( func_num_args() > 6 && ! in_array( $group, $this->no_mc_groups ) ) {
     1291                        if ( $byKey )
     1292                                $value = $this->m->getByKey( $server_key, $derived_key, $cache_cb, $cas_token );
     1293                        else
     1294                                $value = $this->m->get( $derived_key, $cache_cb, $cas_token );
     1295                } else {
     1296                        if ( isset( $this->cache[$derived_key] ) ) {
     1297                                $found = true;
     1298                                return is_object( $this->cache[$derived_key] ) ? clone $this->cache[$derived_key] : $this->cache[$derived_key];
     1299                        } elseif ( in_array( $group, $this->no_mc_groups ) ) {
     1300                                return false;
     1301                        } else {
     1302                                if ( $byKey )
     1303                                        $value = $this->m->getByKey( $server_key, $derived_key );
     1304                                else
     1305                                        $value = $this->m->get( $derived_key );
     1306                        }
     1307                }
     1308
     1309                if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
     1310                        $this->add_to_internal_cache( $derived_key, $value );
     1311                        $found = true;
     1312                }
     1313
     1314                return is_object( $value ) ? clone $value : $value;
     1315        }
     1316
     1317        /**
     1318         * Retrieve object from cache from specified server.
     1319         *
     1320         * Gets an object from cache based on $key, $group and $server_key. In order to fully support the $cache_cb and $cas_token
     1321         * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
     1322         * those values are set, the request is made directly to the memcached server for proper handling of the
     1323         * callback and/or token. Note that the $cas_token variable cannot be directly passed to the function. The
     1324         * variable need to be first defined with a non null value.
     1325         *
     1326         * If using the $cache_cb argument, the new value will always have an expiration of time of 0 (forever). This
     1327         * is a limitation of the Memcached PECL extension.
     1328         *
     1329         * @link http://www.php.net/manual/en/memcached.getbykey.php
     1330         *
     1331         * @param   string          $server_key The key identifying the server to store the value on.
     1332         * @param   string          $key        The key under which to store the value.
     1333         * @param   string          $group      The group value appended to the $key.
     1334         * @param   bool            $force      Whether or not to force a cache invalidation.
     1335         * @param   null|bool       $found      Variable passed by reference to determine if the value was found or not.
     1336         * @param   null|string     $cache_cb   Read-through caching callback.
     1337         * @param   null|float      $cas_token  The variable to store the CAS token in.
     1338         * @return  bool|mixed                  Cached object value.
     1339         */
     1340        public function getByKey( $server_key, $key, $group = 'default', $force = false, &$found = null, $cache_cb = NULL, &$cas_token = NULL ) {
     1341                /**
     1342                 * Need to be careful how "get" is called. If you send $cache_cb, and $cas_token, it will hit memcached.
     1343                 * Only send those args if they were sent to this function.
     1344                 */
     1345                if ( func_num_args() > 5 )
     1346                        return $this->get( $key, $group, $force, $found, $server_key, true, $cache_cb, $cas_token );
     1347                else
     1348                        return $this->get( $key, $group, $force, $found, $server_key, true );
     1349        }
     1350
     1351        /**
     1352         * Request multiple keys without blocking.
     1353         *
     1354         * @link http://www.php.net/manual/en/memcached.getdelayed.php
     1355         *
     1356         * @param   string|array    $keys       Array or string of key(s) to request.
     1357         * @param   string|array    $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
     1358         * @param   bool            $with_cas   Whether to request CAS token values also.
     1359         * @param   null            $value_cb   The result callback or NULL.
     1360         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1361         */
     1362        public function getDelayed( $keys, $groups = 'default', $with_cas = false, $value_cb = NULL ) {
     1363                $derived_keys = $this->buildKeys( $keys, $groups );
     1364                return $this->m->getDelayed( $derived_keys, $with_cas, $value_cb );
     1365        }
     1366
     1367        /**
     1368         * Request multiple keys without blocking from a specified server.
     1369         *
     1370         * @link http://www.php.net/manual/en/memcached.getdelayed.php
     1371         *
     1372         * @param   string          $server_key The key identifying the server to store the value on.
     1373         * @param   string|array    $keys       Array or string of key(s) to request.
     1374         * @param   string|array    $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
     1375         * @param   bool            $with_cas   Whether to request CAS token values also.
     1376         * @param   null            $value_cb   The result callback or NULL.
     1377         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1378         */
     1379        public function getDelayedByKey( $server_key, $keys, $groups = 'default', $with_cas = false, $value_cb = NULL ) {
     1380                $derived_keys = $this->buildKeys( $keys, $groups );
     1381                return $this->m->getDelayedByKey( $server_key, $derived_keys, $with_cas, $value_cb );
     1382        }
     1383
     1384        /**
     1385         * Gets multiple values from memcached in one request.
     1386         *
     1387         * See the buildKeys method definition to understand the $keys/$groups parameters.
     1388         *
     1389         * @link http://www.php.net/manual/en/memcached.getmulti.php
     1390         *
     1391         * @param   array           $keys       Array of keys to retrieve.
     1392         * @param   string|array    $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
     1393         * @param   string          $server_key The key identifying the server to store the value on.
     1394         * @param   null|array      $cas_tokens The variable to store the CAS tokens for the found items.
     1395         * @param   int             $flags      The flags for the get operation.
     1396         * @return  bool|array                  Returns the array of found items or FALSE on failure.
     1397         */
     1398        public function getMulti( $keys, $groups = 'default', $server_key = '', &$cas_tokens = NULL, $flags = NULL ) {
     1399                $derived_keys = $this->buildKeys( $keys, $groups );
     1400
     1401                /**
     1402                 * If either $cas_tokens, or $flags is set, must hit Memcached and bypass runtime cache. Note that
     1403                 * this will purposely ignore no_mc_groups values as they cannot handle CAS tokens or the special
     1404                 * flags; however, if the groups of groups contains a no_mc_group, this is bypassed.
     1405                 */
     1406                if ( func_num_args() > 3 && ! $this->contains_no_mc_group( $groups ) ) {
     1407                        if ( ! empty( $server_key ) )
     1408                                $values = $this->m->getMultiByKey( $server_key, $derived_keys, $cas_tokens, $flags );
     1409                        else
     1410                                $values = $this->m->getMulti( $derived_keys, $cas_tokens, $flags );
     1411                } else {
     1412                        $values = array();
     1413                        $need_to_get = array();
     1414
     1415                        // Pull out values from runtime cache, or mark for retrieval
     1416                        foreach ( $derived_keys as $key ) {
     1417                                if ( isset( $this->cache[$key] ) )
     1418                                        $values[$key] = $this->cache[$key];
     1419                                else
     1420                                        $need_to_get[$key] = $key;
     1421                        }
     1422
     1423                        // Get those keys not found in the runtime cache
     1424                        if ( ! empty( $need_to_get ) ) {
     1425                                if ( ! empty( $server_key ) )
     1426                                        $result = $this->m->getMultiByKey( $server_key, array_keys( $need_to_get ) );
     1427                                else
     1428                                        $result = $this->m->getMulti( array_keys( $need_to_get ) );
     1429                        }
     1430
     1431                        // Merge with values found in runtime cache
     1432                        if ( isset( $result ) && Memcached::RES_SUCCESS === $this->getResultCode() )
     1433                                $values = array_merge( $values, $result );
     1434
     1435                        // If order should be preserved, reorder now
     1436                        if ( ! empty( $need_to_get ) && $flags === Memcached::GET_PRESERVE_ORDER ) {
     1437                                $ordered_values = array();
     1438
     1439                                foreach ( $derived_keys as $key ) {
     1440                                        if ( isset( $values[$key] ) )
     1441                                                $ordered_values[$key] = $values[$key];
     1442                                }
     1443
     1444                                $values = $ordered_values;
     1445                                unset( $ordered_values );
     1446                        }
     1447                }
     1448
     1449                // Add the values to the runtime cache
     1450                $this->cache = array_merge( $this->cache, $values );
     1451
     1452                return $values;
     1453        }
     1454
     1455        /**
     1456         * Gets multiple values from memcached in one request by specified server key.
     1457         *
     1458         * See the buildKeys method definition to understand the $keys/$groups parameters.
     1459         *
     1460         * @link http://www.php.net/manual/en/memcached.getmultibykey.php
     1461         *
     1462         * @param   string          $server_key The key identifying the server to store the value on.
     1463         * @param   array           $keys       Array of keys to retrieve.
     1464         * @param   string|array    $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
     1465         * @param   null|array      $cas_tokens The variable to store the CAS tokens for the found items.
     1466         * @param   int             $flags      The flags for the get operation.
     1467         * @return  bool|array                  Returns the array of found items or FALSE on failure.
     1468         */
     1469        public function getMultiByKey( $server_key, $keys, $groups = 'default', &$cas_tokens = NULL, $flags = NULL ) {
     1470                /**
     1471                 * Need to be careful how "getMulti" is called. If you send $cache_cb, and $cas_token, it will hit memcached.
     1472                 * Only send those args if they were sent to this function.
     1473                 */
     1474                if ( func_num_args() > 3 )
     1475                        return $this->getMulti( $keys, $groups, $server_key, $cas_tokens, $flags );
     1476                else
     1477                        return $this->getMulti( $keys, $groups, $server_key );
     1478        }
     1479
     1480        /**
     1481         * Retrieve a Memcached option value.
     1482         *
     1483         * @link http://www.php.net/manual/en/memcached.getoption.php
     1484         *
     1485         * @param   int         $option     One of the Memcached::OPT_* constants.
     1486         * @return  mixed                   Returns the value of the requested option, or FALSE on error.
     1487         */
     1488        public function getOption( $option ) {
     1489                return $this->m->getOption( $option );
     1490        }
     1491
     1492        /**
     1493         * Return the result code of the last option.
     1494         *
     1495         * @link http://www.php.net/manual/en/memcached.getresultcode.php
     1496         *
     1497         * @return  int     Result code of the last Memcached operation.
     1498         */
     1499        public function getResultCode() {
     1500            return $this->m->getResultCode();
     1501        }
     1502
     1503        /**
     1504         * Return the message describing the result of the last operation.
     1505         *
     1506         * @link    http://www.php.net/manual/en/memcached.getresultmessage.php
     1507         *
     1508         * @return  string      Message describing the result of the last Memcached operation.
     1509         */
     1510        public function getResultMessage() {
     1511            return $this->m->getResultMessage();
     1512        }
     1513
     1514        /**
     1515         * Get server information by key.
     1516         *
     1517         * @link    http://www.php.net/manual/en/memcached.getserverbykey.php
     1518         *
     1519         * @param   string      $server_key     The key identifying the server to store the value on.
     1520         * @return  array                       Array with host, post, and weight on success, FALSE on failure.
     1521         */
     1522        public function getServerByKey( $server_key ) {
     1523                return $this->m->getServerByKey( $server_key );
     1524        }
     1525
     1526        /**
     1527         * Get the list of servers in the pool.
     1528         *
     1529         * @link    http://www.php.net/manual/en/memcached.getserverlist.php
     1530         *
     1531         * @return  array       The list of all servers in the server pool.
     1532         */
     1533        public function getServerList() {
     1534                return $this->m->getServerList();
     1535        }
     1536
     1537        /**
     1538     * Get server pool statistics.
     1539         *
     1540         * @link    http://www.php.net/manual/en/memcached.getstats.php
     1541         *
     1542         * @return  array       Array of server statistics, one entry per server.
     1543         */
     1544        public function getStats() {
     1545                return $this->m->getStats();
     1546        }
     1547
     1548        /**
     1549         * Get server pool memcached version information.
     1550         *
     1551         * @link    http://www.php.net/manual/en/memcached.getversion.php
     1552         *
     1553         * @return  array       Array of server versions, one entry per server.
     1554         */
     1555        public function getVersion() {
     1556                return $this->m->getVersion();
     1557        }
     1558
     1559        /**
     1560         * Increment a numeric item's value.
     1561         *
     1562         * @link http://www.php.net/manual/en/memcached.increment.php
     1563         *
     1564         * @param   string      $key        The key under which to store the value.
     1565         * @param   int         $offset     The amount by which to increment the item's value.
     1566         * @param   string      $group      The group value appended to the $key.
     1567         * @return  int|bool                Returns item's new value on success or FALSE on failure.
     1568         */
     1569        public function increment( $key, $offset = 1, $group = 'default' ) {
     1570                $derived_key = $this->buildKey( $key, $group );
     1571
     1572                // Increment values in no_mc_groups
     1573                if ( in_array( $group, $this->no_mc_groups ) ) {
     1574
     1575                        // Only increment if the key already exists and the number is currently 0 or greater (mimics memcached behavior)
     1576                        if ( isset( $this->cache[$derived_key] ) &&  $this->cache[$derived_key] >= 0 ) {
     1577
     1578                                // If numeric, add; otherwise, consider it 0 and do nothing
     1579                                if ( is_numeric( $this->cache[$derived_key] ) )
     1580                                        $this->cache[$derived_key] += (int) $offset;
     1581                                else
     1582                                        $this->cache[$derived_key] = 0;
     1583
     1584                                // Returned value cannot be less than 0
     1585                                if ( $this->cache[$derived_key] < 0 )
     1586                                        $this->cache[$derived_key] = 0;
     1587
     1588                                return $this->cache[$derived_key];
     1589                        } else {
     1590                                return false;
     1591                        }
     1592                }
     1593
     1594                $result = $this->m->increment( $derived_key, $offset );
     1595
     1596                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1597                        $this->add_to_internal_cache( $derived_key, $result );
     1598
     1599                return $result;
     1600        }
     1601
     1602        /**
     1603         * Synonymous with $this->incr.
     1604         *
     1605         * Certain plugins expect an "incr" method on the $wp_object_cache object (e.g., Batcache). Since the original
     1606         * version of this library matched names to the memcached methods, the "incr" method was missing. Adding this
     1607         * method restores compatibility with plugins expecting an "incr" method.
     1608         *
     1609         * @param   string      $key        The key under which to store the value.
     1610         * @param   int         $offset     The amount by which to increment the item's value.
     1611         * @param   string      $group      The group value appended to the $key.
     1612         * @return  int|bool                Returns item's new value on success or FALSE on failure.
     1613         */
     1614        public function incr( $key, $offset = 1, $group = 'default' ) {
     1615                return $this->increment( $key, $offset, $group );
     1616        }
     1617
     1618        /**
     1619         * Prepend data to an existing item.
     1620         *
     1621         * This method should throw an error if it is used with compressed data. This is an expected behavior.
     1622         * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
     1623         * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
     1624         * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
     1625         * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
     1626         * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
     1627         * that prepends only occur with data of the same type.
     1628         *
     1629         * @link    http://www.php.net/manual/en/memcached.prepend.php
     1630         *
     1631         * @param   string    $key          The key under which to store the value.
     1632         * @param   string    $value        Must be string as prepending mixed values is not well-defined.
     1633         * @param   string    $group        The group value prepended to the $key.
     1634         * @param   string    $server_key   The key identifying the server to store the value on.
     1635         * @param   bool      $byKey        True to store in internal cache by key; false to not store by key
     1636         * @return  bool                    Returns TRUE on success or FALSE on failure.
     1637         */
     1638        public function prepend( $key, $value, $group = 'default', $server_key = '', $byKey = false ) {
     1639                if ( ! is_string( $value ) && ! is_int( $value ) && ! is_float( $value ) )
     1640                        return false;
     1641
     1642                $derived_key = $this->buildKey( $key, $group );
     1643
     1644                // If group is a non-Memcached group, prepend to runtime cache value, not Memcached
     1645                if ( in_array( $group, $this->no_mc_groups ) ) {
     1646                        if ( ! isset( $this->cache[$derived_key] ) )
     1647                                return false;
     1648
     1649                        $combined = $this->combine_values( $this->cache[$derived_key], $value, 'pre' );
     1650                        $this->add_to_internal_cache( $derived_key, $combined );
     1651                        return true;
     1652                }
     1653
     1654                // Append to Memcached value
     1655                if ( $byKey )
     1656                        $result = $this->m->prependByKey( $server_key, $derived_key, $value );
     1657                else
     1658                        $result = $this->m->prepend( $derived_key, $value );
     1659
     1660                // Store in runtime cache if add was successful
     1661                if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
     1662                        $combined = $this->combine_values( $this->cache[$derived_key], $value, 'pre' );
     1663                        $this->add_to_internal_cache( $derived_key, $combined );
     1664                }
     1665
     1666                return $result;
     1667        }
     1668
     1669        /**
     1670         * Append data to an existing item by server key.
     1671         *
     1672         * This method should throw an error if it is used with compressed data. This is an expected behavior.
     1673         * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
     1674         * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
     1675         * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
     1676         * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
     1677         * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
     1678         * that prepends only occur with data of the same type.
     1679         *
     1680         * @link    http://www.php.net/manual/en/memcached.prependbykey.php
     1681         *
     1682         * @param   string    $server_key   The key identifying the server to store the value on.
     1683         * @param   string    $key          The key under which to store the value.
     1684         * @param   string    $value        Must be string as prepending mixed values is not well-defined.
     1685         * @param   string    $group        The group value prepended to the $key.
     1686         * @return  bool                    Returns TRUE on success or FALSE on failure.
     1687         */
     1688        public function prependByKey( $server_key, $key, $value, $group = 'default' ) {
     1689                return $this->prepend( $key, $value, $group, $server_key, true );
     1690        }
     1691
     1692        /**
     1693         * Replaces a value in cache.
     1694         *
     1695         * This method is similar to "add"; however, is does not successfully set a value if
     1696         * the object's key is not already set in cache.
     1697         *
     1698         * @link    http://www.php.net/manual/en/memcached.replace.php
     1699         *
     1700         * @param   string      $server_key     The key identifying the server to store the value on.
     1701         * @param   string      $key            The key under which to store the value.
     1702         * @param   mixed       $value          The value to store.
     1703         * @param   string      $group          The group value appended to the $key.
     1704         * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
     1705         * @param   int         $expiration     The expiration time, defaults to 0.
     1706         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1707         */
     1708        public function replace( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
     1709                $derived_key = $this->buildKey( $key, $group );
     1710                $expiration  = $this->sanitize_expiration( $expiration );
     1711
     1712                // If group is a non-Memcached group, save to runtime cache, not Memcached
     1713                if ( in_array( $group, $this->no_mc_groups ) ) {
     1714
     1715                        // Replace won't save unless the key already exists; mimic this behavior here
     1716                        if ( ! isset( $this->cache[$derived_key] ) )
     1717                                return false;
     1718
     1719                        $this->cache[$derived_key] = $value;
     1720                        return true;
     1721                }
     1722
     1723                // Save to Memcached
     1724                if ( $byKey )
     1725                        $result = $this->m->replaceByKey( $server_key, $derived_key, $value, $expiration );
     1726                else
     1727                        $result = $this->m->replace( $derived_key, $value, $expiration );
     1728
     1729                // Store in runtime cache if add was successful
     1730                if ( Memcached::RES_SUCCESS === $this->getResultCode() )
     1731                        $this->add_to_internal_cache( $derived_key, $value );
     1732
     1733                return $result;
     1734        }
     1735
     1736        /**
     1737         * Replaces a value in cache on a specific server.
     1738         *
     1739         * This method is similar to "addByKey"; however, is does not successfully set a value if
     1740         * the object's key is not already set in cache.
     1741         *
     1742         * @link    http://www.php.net/manual/en/memcached.addbykey.php
     1743         *
     1744         * @param   string      $server_key     The key identifying the server to store the value on.
     1745         * @param   string      $key            The key under which to store the value.
     1746         * @param   mixed       $value          The value to store.
     1747         * @param   string      $group          The group value appended to the $key.
     1748         * @param   int         $expiration     The expiration time, defaults to 0.
     1749         * @return  bool                        Returns TRUE on success or FALSE on failure.
     1750         */
     1751        public function replaceByKey( $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
     1752                return $this->replace( $key, $value, $group, $expiration, $server_key, true );
     1753        }
     1754
     1755        /**
     1756         * Sets a value in cache.
     1757         *
     1758         * The value is set whether or not this key already exists in memcached.
     1759         *
     1760         * @link http://www.php.net/manual/en/memcached.set.php
     1761         *
     1762         * @param   string&nbs