Commit 9d4a0d31 authored by lucha's avatar lucha
Browse files

Wordpress 4.8.1

parent 1db6da7d
......@@ -43,6 +43,28 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
<a href="freedoms.php" class="nav-tab"><?php _e( 'Freedoms' ); ?></a>
</h2>
<div class="changelog point-releases">
<h3><?php _e( 'Maintenance Release' ); ?></h3>
<p>
<?php
printf(
/* translators: 1: WordPress version number, 2: plural number of bugs. */
_n(
'<strong>Version %1$s</strong> addressed %2$s bug.',
'<strong>Version %1$s</strong> addressed %2$s bugs.',
29
),
'4.8.1',
number_format_i18n( 29 )
);
?>
<?php
/* translators: %s: Codex URL */
printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.8.1' );
?>
</p>
</div>
<div class="feature-section one-col">
<div class="col">
<h2><?php _e( 'An Update with You in Mind' ); ?></h2>
......
......@@ -1644,6 +1644,8 @@ body.full-overlay-active {
min-width: 299px;
max-width: 599px;
width: 18%;
width: -webkit-calc( 18% - 1px );
width: calc( 18% - 1px );
height: 45px;
border-top: 1px solid #ddd;
background: #eee;
......
This diff is collapsed.
......@@ -1644,6 +1644,8 @@ body.full-overlay-active {
min-width: 299px;
max-width: 599px;
width: 18%;
width: -webkit-calc( 18% - 1px );
width: calc( 18% - 1px );
height: 45px;
border-top: 1px solid #ddd;
background: #eee;
......
This diff is collapsed.
......@@ -619,6 +619,29 @@ div#widgets-right .widget-top:hover,
cursor: move;
}
/* =Specific widget styling
-------------------------------------------------------------- */
.text-widget-fields {
position: relative;
}
.text-widget-fields [hidden] {
display: none;
}
.text-widget-fields .wp-pointer.wp-pointer-top {
position: absolute;
z-index: 3;
top: 100px;
left: 10px;
right: 10px;
}
.text-widget-fields .wp-pointer .wp-pointer-arrow {
right: auto;
left: 15px;
}
.text-widget-fields .wp-pointer .wp-pointer-buttons {
line-height: 1.4em;
}
/* =Media Queries
-------------------------------------------------------------- */
......
This diff is collapsed.
......@@ -619,6 +619,29 @@ div#widgets-right .widget-top:hover,
cursor: move;
}
/* =Specific widget styling
-------------------------------------------------------------- */
.text-widget-fields {
position: relative;
}
.text-widget-fields [hidden] {
display: none;
}
.text-widget-fields .wp-pointer.wp-pointer-top {
position: absolute;
z-index: 3;
top: 100px;
right: 10px;
left: 10px;
}
.text-widget-fields .wp-pointer .wp-pointer-arrow {
left: auto;
right: 15px;
}
.text-widget-fields .wp-pointer .wp-pointer-buttons {
line-height: 1.4em;
}
/* =Media Queries
-------------------------------------------------------------- */
......
This diff is collapsed.
......@@ -319,6 +319,11 @@ class WP_Comments_List_Table extends WP_List_Table {
*/
protected function extra_tablenav( $which ) {
global $comment_status, $comment_type;
static $has_items;
if ( ! isset( $has_items ) ) {
$has_items = $this->has_items();
}
?>
<div class="alignleft actions">
<?php
......@@ -354,7 +359,7 @@ class WP_Comments_List_Table extends WP_List_Table {
submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
}
if ( ( 'spam' === $comment_status || 'trash' === $comment_status ) && current_user_can( 'moderate_comments' ) && $this->has_items() ) {
if ( ( 'spam' === $comment_status || 'trash' === $comment_status ) && current_user_can( 'moderate_comments' ) && $has_items ) {
wp_nonce_field( 'bulk-destroy', '_destroy_nonce' );
$title = ( 'spam' === $comment_status ) ? esc_attr__( 'Empty Spam' ) : esc_attr__( 'Empty Trash' );
submit_button( $title, 'apply', 'delete_all', false );
......
......@@ -100,8 +100,8 @@ jQuery(document).ready(function() {
return;
jQuery('#permalink_structure').val( this.value );
});
jQuery('#permalink_structure').focus(function() {
jQuery("#custom_selection").attr('checked', 'checked');
jQuery( '#permalink_structure' ).on( 'click input', function() {
jQuery( '#custom_selection' ).prop( 'checked', true );
});
});
</script>
......
......@@ -5507,6 +5507,13 @@
} );
} ());
// Make sure TinyMCE dialogs appear above Customizer UI.
$( document ).one( 'wp-before-tinymce-init', function() {
if ( ! window.tinymce.ui.FloatPanel.zIndex || window.tinymce.ui.FloatPanel.zIndex < 500001 ) {
window.tinymce.ui.FloatPanel.zIndex = 500001;
}
} );
api.trigger( 'ready' );
});
......
This diff is collapsed.
......@@ -2229,8 +2229,7 @@
}
} );
control.container.find( '.menu-delete-item' ).on( 'click', function( event ) {
event.stopPropagation();
control.container.find( '.menu-delete-item .button-link-delete' ).on( 'click', function( event ) {
event.preventDefault();
control.setting.set( false );
});
......
This diff is collapsed.
......@@ -184,6 +184,12 @@ wp.mediaWidgets = ( function( $ ) {
return;
}
// If video, test for Vimeo and YouTube, otherwise, renderFail(). This should be removed once #34115 is resolved.
if ( 'video' === this.controller.options.mimeType && ! /vimeo|youtu\.?be/.test( urlParser.host ) ) {
embedLinkView.renderFail();
return;
}
embedLinkView.dfd = $.ajax({
url: wp.media.view.settings.oEmbedProxyUrl,
data: {
......@@ -429,7 +435,8 @@ wp.mediaWidgets = ( function( $ ) {
*
* @param {Object} options - Options.
* @param {Backbone.Model} options.model - Model.
* @param {jQuery} options.el - Control container element.
* @param {jQuery} options.el - Control field container element.
* @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
* @returns {void}
*/
initialize: function initialize( options ) {
......@@ -437,12 +444,19 @@ wp.mediaWidgets = ( function( $ ) {
Backbone.View.prototype.initialize.call( control, options );
if ( ! control.el ) {
throw new Error( 'Missing options.el' );
}
if ( ! ( control.model instanceof component.MediaWidgetModel ) ) {
throw new Error( 'Missing options.model' );
}
if ( ! options.el ) {
throw new Error( 'Missing options.el' );
}
if ( ! options.syncContainer ) {
throw new Error( 'Missing options.syncContainer' );
}
control.syncContainer = options.syncContainer;
control.$el.addClass( 'media-widget-control' );
// Allow methods to be passed in with control context preserved.
_.bindAll( control, 'syncModelToInputs', 'render', 'updateSelectedAttachment', 'renderPreview' );
......@@ -547,7 +561,7 @@ wp.mediaWidgets = ( function( $ ) {
*/
syncModelToInputs: function syncModelToInputs() {
var control = this;
control.$el.next( '.widget-content' ).find( '.media-widget-instance-property' ).each( function() {
control.syncContainer.find( '.media-widget-instance-property' ).each( function() {
var input = $( this ), value;
value = control.model.get( input.data( 'property' ) );
if ( _.isUndefined( value ) ) {
......@@ -1003,9 +1017,8 @@ wp.mediaWidgets = ( function( $ ) {
* @returns {void}
*/
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
var widgetContent, controlContainer, widgetForm, idBase, ControlConstructor, ModelConstructor, modelAttributes, widgetControl, widgetModel, widgetId, widgetInside, animatedCheckDelay = 50, renderWhenAnimationDone;
var fieldContainer, syncContainer, widgetForm, idBase, ControlConstructor, ModelConstructor, modelAttributes, widgetControl, widgetModel, widgetId, widgetInside, animatedCheckDelay = 50, renderWhenAnimationDone;
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen.
widgetContent = widgetForm.find( '> .widget-content' );
idBase = widgetForm.find( '> .id_base' ).val();
widgetId = widgetForm.find( '> .widget-id' ).val();
......@@ -1032,8 +1045,9 @@ wp.mediaWidgets = ( function( $ ) {
* components", the JS template is rendered outside of the normal form
* container.
*/
controlContainer = $( '<div class="media-widget-control"></div>' );
widgetContent.before( controlContainer );
fieldContainer = $( '<div></div>' );
syncContainer = widgetContainer.find( '.widget-content:first' );
syncContainer.before( fieldContainer );
/*
* Sync the widget instance model attributes onto the hidden inputs that widgets currently use to store the state.
......@@ -1041,7 +1055,7 @@ wp.mediaWidgets = ( function( $ ) {
* from the start, without having to sync with hidden fields. See <https://core.trac.wordpress.org/ticket/33507>.
*/
modelAttributes = {};
widgetContent.find( '.media-widget-instance-property' ).each( function() {
syncContainer.find( '.media-widget-instance-property' ).each( function() {
var input = $( this );
modelAttributes[ input.data( 'property' ) ] = input.val();
});
......@@ -1050,7 +1064,8 @@ wp.mediaWidgets = ( function( $ ) {
widgetModel = new ModelConstructor( modelAttributes );
widgetControl = new ControlConstructor({
el: controlContainer,
el: fieldContainer,
syncContainer: syncContainer,
model: widgetModel
});
......@@ -1078,6 +1093,51 @@ wp.mediaWidgets = ( function( $ ) {
component.widgetControls[ widgetModel.get( 'widget_id' ) ] = widgetControl;
};
/**
* Setup widget in accessibility mode.
*
* @returns {void}
*/
component.setupAccessibleMode = function setupAccessibleMode() {
var widgetForm, widgetId, idBase, widgetControl, ControlConstructor, ModelConstructor, modelAttributes, fieldContainer, syncContainer;
widgetForm = $( '.editwidget > form' );
if ( 0 === widgetForm.length ) {
return;
}
idBase = widgetForm.find( '> .widget-control-actions > .id_base' ).val();
ControlConstructor = component.controlConstructors[ idBase ];
if ( ! ControlConstructor ) {
return;
}
widgetId = widgetForm.find( '> .widget-control-actions > .widget-id' ).val();
ModelConstructor = component.modelConstructors[ idBase ] || component.MediaWidgetModel;
fieldContainer = $( '<div></div>' );
syncContainer = widgetForm.find( '> .widget-inside' );
syncContainer.before( fieldContainer );
modelAttributes = {};
syncContainer.find( '.media-widget-instance-property' ).each( function() {
var input = $( this );
modelAttributes[ input.data( 'property' ) ] = input.val();
});
modelAttributes.widget_id = widgetId;
widgetControl = new ControlConstructor({
el: fieldContainer,
syncContainer: syncContainer,
model: new ModelConstructor( modelAttributes )
});
component.modelCollection.add( [ widgetControl.model ] );
component.widgetControls[ widgetControl.model.get( 'widget_id' ) ] = widgetControl;
widgetControl.render();
};
/**
* Sync widget instance data sanitized from server back onto widget model.
*
......@@ -1146,6 +1206,11 @@ wp.mediaWidgets = ( function( $ ) {
var widgetContainer = $( this );
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
});
// Accessibility mode.
$( window ).on( 'load', function() {
component.setupAccessibleMode();
});
});
};
......
This diff is collapsed.
......@@ -3,7 +3,9 @@
wp.textWidgets = ( function( $ ) {
'use strict';
var component = {};
var component = {
dismissedPointers: []
};
/**
* Text widget control.
......@@ -24,9 +26,9 @@ wp.textWidgets = ( function( $ ) {
/**
* Initialize.
*
* @param {Object} options - Options.
* @param {Backbone.Model} options.model - Model.
* @param {jQuery} options.el - Control container element.
* @param {Object} options - Options.
* @param {jQuery} options.el - Control field container element.
* @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
* @returns {void}
*/
initialize: function initialize( options ) {
......@@ -35,42 +37,97 @@ wp.textWidgets = ( function( $ ) {
if ( ! options.el ) {
throw new Error( 'Missing options.el' );
}
if ( ! options.syncContainer ) {
throw new Error( 'Missing options.syncContainer' );
}
Backbone.View.prototype.initialize.call( control, options );
control.syncContainer = options.syncContainer;
control.$el.addClass( 'text-widget-fields' );
control.$el.html( wp.template( 'widget-text-control-fields' ) );
control.customHtmlWidgetPointer = control.$el.find( '.wp-pointer.custom-html-widget-pointer' );
if ( control.customHtmlWidgetPointer.length ) {
control.customHtmlWidgetPointer.find( '.close' ).on( 'click', function( event ) {
event.preventDefault();
control.customHtmlWidgetPointer.hide();
$( '#' + control.fields.text.attr( 'id' ) + '-html' ).focus();
control.dismissPointers( [ 'text_widget_custom_html' ] );
});
control.customHtmlWidgetPointer.find( '.add-widget' ).on( 'click', function( event ) {
event.preventDefault();
control.customHtmlWidgetPointer.hide();
control.openAvailableWidgetsPanel();
});
}
/*
* Create a container element for the widget control fields.
* This is inserted into the DOM immediately before the the .widget-content
* element because the contents of this element are essentially "managed"
* by PHP, where each widget update cause the entire element to be emptied
* and replaced with the rendered output of WP_Widget::form() which is
* sent back in Ajax request made to save/update the widget instance.
* To prevent a "flash of replaced DOM elements and re-initialized JS
* components", the JS template is rendered outside of the normal form
* container.
*/
control.fieldContainer = $( '<div class="text-widget-fields"></div>' );
control.fieldContainer.html( wp.template( 'widget-text-control-fields' ) );
control.widgetContentContainer = control.$el.find( '.widget-content:first' );
control.widgetContentContainer.before( control.fieldContainer );
control.pasteHtmlPointer = control.$el.find( '.wp-pointer.paste-html-pointer' );
if ( control.pasteHtmlPointer.length ) {
control.pasteHtmlPointer.find( '.close' ).on( 'click', function( event ) {
event.preventDefault();
control.pasteHtmlPointer.hide();
control.editor.focus();
control.dismissPointers( [ 'text_widget_custom_html', 'text_widget_paste_html' ] );
});
}
control.fields = {
title: control.fieldContainer.find( '.title' ),
text: control.fieldContainer.find( '.text' )
title: control.$el.find( '.title' ),
text: control.$el.find( '.text' )
};
// Sync input fields to hidden sync fields which actually get sent to the server.
_.each( control.fields, function( fieldInput, fieldName ) {
fieldInput.on( 'input change', function updateSyncField() {
var syncInput = control.widgetContentContainer.find( 'input[type=hidden].' + fieldName );
if ( syncInput.val() !== $( this ).val() ) {
syncInput.val( $( this ).val() );
var syncInput = control.syncContainer.find( 'input[type=hidden].' + fieldName );
if ( syncInput.val() !== fieldInput.val() ) {
syncInput.val( fieldInput.val() );
syncInput.trigger( 'change' );
}
});
// Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event.
fieldInput.val( control.widgetContentContainer.find( 'input[type=hidden].' + fieldName ).val() );
fieldInput.val( control.syncContainer.find( 'input[type=hidden].' + fieldName ).val() );
});
},
/**
* Dismiss pointers for Custom HTML widget.
*
* @since 4.8.1
*
* @param {Array} pointers Pointer IDs to dismiss.
* @returns {void}
*/
dismissPointers: function dismissPointers( pointers ) {
_.each( pointers, function( pointer ) {
wp.ajax.post( 'dismiss-wp-pointer', {
pointer: pointer
});
component.dismissedPointers.push( pointer );
});
},
/**
* Open available widgets panel.
*
* @since 4.8.1
* @returns {void}
*/
openAvailableWidgetsPanel: function openAvailableWidgetsPanel() {
var sidebarControl;
wp.customize.section.each( function( section ) {
if ( section.extended( wp.customize.Widgets.SidebarSection ) && section.expanded() ) {
sidebarControl = wp.customize.control( 'sidebars_widgets[' + section.params.sidebarId + ']' );
}
});
if ( ! sidebarControl ) {
return;
}
setTimeout( function() { // Timeout to prevent click event from causing panel to immediately collapse.
wp.customize.Widgets.availableWidgetsPanel.open( sidebarControl );
wp.customize.Widgets.availableWidgetsPanel.$search.val( 'HTML' ).trigger( 'keyup' );
});
},
......@@ -87,11 +144,11 @@ wp.textWidgets = ( function( $ ) {
var control = this, syncInput;
if ( ! control.fields.title.is( document.activeElement ) ) {
syncInput = control.widgetContentContainer.find( 'input[type=hidden].title' );
syncInput = control.syncContainer.find( 'input[type=hidden].title' );
control.fields.title.val( syncInput.val() );
}
syncInput = control.widgetContentContainer.find( 'input[type=hidden].text' );
syncInput = control.syncContainer.find( 'input[type=hidden].text' );
if ( control.fields.text.is( ':visible' ) ) {
if ( ! control.fields.text.is( document.activeElement ) ) {
control.fields.text.val( syncInput.val() );
......@@ -107,23 +164,77 @@ wp.textWidgets = ( function( $ ) {
* @returns {void}
*/
initializeEditor: function initializeEditor() {
var control = this, changeDebounceDelay = 1000, id, textarea, restoreTextMode = false;
var control = this, changeDebounceDelay = 1000, id, textarea, triggerChangeIfDirty, restoreTextMode = false, needsTextareaChangeTrigger = false;
textarea = control.fields.text;
id = textarea.attr( 'id' );
/**
* Trigger change if dirty.
*
* @returns {void}
*/
triggerChangeIfDirty = function() {
var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced.
if ( control.editor.isDirty() ) {
/*
* Account for race condition in customizer where user clicks Save & Publish while
* focus was just previously given to to the editor. Since updates to the editor
* are debounced at 1 second and since widget input changes are only synced to
* settings after 250ms, the customizer needs to be put into the processing
* state during the time between the change event is triggered and updateWidget
* logic starts. Note that the debounced update-widget request should be able
* to be removed with the removal of the update-widget request entirely once
* widgets are able to mutate their own instance props directly in JS without
* having to make server round-trips to call the respective WP_Widget::update()
* callbacks. See <https://core.trac.wordpress.org/ticket/33507>.
*/
if ( wp.customize && wp.customize.state ) {
wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 );
_.delay( function() {
wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 );
}, updateWidgetBuffer );
}
if ( ! control.editor.isHidden() ) {
control.editor.save();
}
}
// Trigger change on textarea when it is dirty for sake of widgets in the Customizer needing to sync form inputs to setting models.
if ( needsTextareaChangeTrigger ) {
textarea.trigger( 'change' );
needsTextareaChangeTrigger = false;
}
};
// Just-in-time force-update the hidden input fields.
control.syncContainer.closest( '.widget' ).find( '[name=savewidget]:first' ).on( 'click', function onClickSaveButton() {
triggerChangeIfDirty();
});
/**
* Build (or re-build) the visual editor.
*
* @returns {void}
*/
function buildEditor() {
var editor, triggerChangeIfDirty, onInit;
var editor, onInit, showPointerElement;
// Abort building if the textarea is gone, likely due to the widget having been deleted entirely.
if ( ! document.getElementById( id ) ) {
return;
}
// The user has disabled TinyMCE.
if ( typeof window.tinymce === 'undefined' ) {
wp.editor.initialize( id, {
quicktags: true
});
return;
}
// Destroy any existing editor so that it can be re-initialized after a widget-updated event.
if ( tinymce.get( id ) ) {
restoreTextMode = tinymce.get( id ).isHidden();
......@@ -137,6 +248,20 @@ wp.textWidgets = ( function( $ ) {
quicktags: true
});
/**
* Show a pointer, focus on dismiss, and speak the contents for a11y.
*
* @param {jQuery} pointerElement Pointer element.
* @returns {void}
*/
showPointerElement = function( pointerElement ) {
pointerElement.show();
pointerElement.find( '.close' ).focus();
wp.a11y.speak( pointerElement.find( 'h3, p' ).map( function() {
return $( this ).text();
} ).get().join( '\n\n' ) );
};
editor = window.tinymce.get( id );
if ( ! editor ) {
throw new Error( 'Failed to initialize editor' );
......@@ -150,8 +275,36 @@ wp.textWidgets = ( function( $ ) {
// If a prior mce instance was replaced, and it was in text mode, toggle to text mode.