Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd46042e3c | ||
|
|
e10af224e2 | ||
|
|
a9897ef218 | ||
|
|
546dcf7fed | ||
|
|
30f39b93be | ||
|
|
fee9368d46 | ||
|
|
b9d2c50edd | ||
|
|
be70069a0a | ||
|
|
7eccdc89e7 | ||
|
|
7cdc8af10c | ||
|
|
cbfc476b43 | ||
|
|
a17fcecfda | ||
|
|
fd423f720e | ||
|
|
74314b2ddb | ||
|
|
6be40b7a13 | ||
|
|
96da852a31 | ||
|
|
6ce522414f | ||
|
|
fc475c2d71 | ||
|
|
ff0656e37c | ||
|
|
8575d4e180 | ||
|
|
aef4b48187 | ||
|
|
940c2238ec | ||
|
|
ec08f02aa4 | ||
|
|
471cb97374 | ||
|
|
e875520cec | ||
|
|
d68bbe13cb | ||
|
|
db65c15aba | ||
|
|
dd7c2798dc | ||
|
|
3f4babe28b | ||
|
|
d32185545b | ||
|
|
dbdd48e05e | ||
|
|
2c82efe8ca | ||
|
|
c4ac9d0ade | ||
|
|
e5f6b9078e | ||
|
|
2e43bdea3e | ||
|
|
5f3b1410fd | ||
|
|
3a05029032 | ||
|
|
2d4418b905 | ||
|
|
c4809250ca | ||
|
|
56f581b195 | ||
|
|
e88ec329f7 | ||
|
|
b59b5b9ab8 | ||
|
|
93db53b77d |
@@ -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;
|
||||
|
||||
2
wp-admin/css/themes-rtl.min.css
vendored
2
wp-admin/css/themes-rtl.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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;
|
||||
|
||||
2
wp-admin/css/themes.min.css
vendored
2
wp-admin/css/themes.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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
|
||||
-------------------------------------------------------------- */
|
||||
|
||||
|
||||
2
wp-admin/css/widgets-rtl.min.css
vendored
2
wp-admin/css/widgets-rtl.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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
|
||||
-------------------------------------------------------------- */
|
||||
|
||||
|
||||
2
wp-admin/css/widgets.min.css
vendored
2
wp-admin/css/widgets.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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' );
|
||||
});
|
||||
|
||||
|
||||
2
wp-admin/js/customize-controls.min.js
vendored
2
wp-admin/js/customize-controls.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -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 );
|
||||
});
|
||||
|
||||
2
wp-admin/js/customize-nav-menus.min.js
vendored
2
wp-admin/js/customize-nav-menus.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -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();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
2
wp-admin/js/widgets/media-widgets.min.js
vendored
2
wp-admin/js/widgets/media-widgets.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -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;
|
||||
|
||||
/*
|
||||
* 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.$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();
|
||||
});
|
||||
}
|
||||
|
||||
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.
|
||||
if ( restoreTextMode ) {
|
||||
switchEditors.go( id, 'toggle' );
|
||||
switchEditors.go( id, 'html' );
|
||||
}
|
||||
|
||||
// Show the pointer.
|
||||
$( '#' + id + '-html' ).on( 'click', function() {
|
||||
control.pasteHtmlPointer.hide(); // Hide the HTML pasting pointer.
|
||||
|
||||
if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_custom_html' ) ) {
|
||||
return;
|
||||
}
|
||||
showPointerElement( control.customHtmlWidgetPointer );
|
||||
});
|
||||
|
||||
// Hide the pointer when switching tabs.
|
||||
$( '#' + id + '-tmce' ).on( 'click', function() {
|
||||
control.customHtmlWidgetPointer.hide();
|
||||
});
|
||||
|
||||
// Show pointer when pasting HTML.
|
||||
editor.on( 'pastepreprocess', function( event ) {
|
||||
var content = event.content;
|
||||
if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_paste_html' ) || ! content || ! /<\w+.*?>/.test( content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the pointer after a slight delay so the user sees what they pasted.
|
||||
_.delay( function() {
|
||||
showPointerElement( control.pasteHtmlPointer );
|
||||
}, 250 );
|
||||
});
|
||||
};
|
||||
|
||||
if ( editor.initialized ) {
|
||||
@@ -161,38 +314,19 @@ wp.textWidgets = ( function( $ ) {
|
||||
}
|
||||
|
||||
control.editorFocused = false;
|
||||
triggerChangeIfDirty = function() {
|
||||
var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced.
|
||||
if ( 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( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 );
|
||||
_.delay( function() {
|
||||
wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 );
|
||||
}, updateWidgetBuffer );
|
||||
}
|
||||
|
||||
editor.save();
|
||||
textarea.trigger( 'change' );
|
||||
}
|
||||
};
|
||||
editor.on( 'focus', function() {
|
||||
editor.on( 'focus', function onEditorFocus() {
|
||||
control.editorFocused = true;
|
||||
});
|
||||
editor.on( 'paste', function onEditorPaste() {
|
||||
editor.setDirty( true ); // Because pasting doesn't currently set the dirty state.
|
||||
triggerChangeIfDirty();
|
||||
});
|
||||
editor.on( 'NodeChange', function onNodeChange() {
|
||||
needsTextareaChangeTrigger = true;
|
||||
});
|
||||
editor.on( 'NodeChange', _.debounce( triggerChangeIfDirty, changeDebounceDelay ) );
|
||||
editor.on( 'blur', function() {
|
||||
editor.on( 'blur hide', function onEditorBlur() {
|
||||
control.editorFocused = false;
|
||||
triggerChangeIfDirty();
|
||||
});
|
||||
@@ -219,7 +353,7 @@ wp.textWidgets = ( function( $ ) {
|
||||
* @returns {void}
|
||||
*/
|
||||
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
|
||||
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, widgetInside, renderWhenAnimationDone;
|
||||
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, widgetInside, renderWhenAnimationDone, fieldContainer, syncContainer;
|
||||
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen.
|
||||
|
||||
idBase = widgetForm.find( '> .id_base' ).val();
|
||||
@@ -228,13 +362,34 @@ wp.textWidgets = ( function( $ ) {
|
||||
}
|
||||
|
||||
// Prevent initializing already-added widgets.
|
||||
widgetId = widgetForm.find( '> .widget-id' ).val();
|
||||
widgetId = widgetForm.find( '.widget-id' ).val();
|
||||
if ( component.widgetControls[ widgetId ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bypass using TinyMCE when widget is in legacy mode.
|
||||
if ( ! widgetForm.find( '.visual' ).val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
fieldContainer = $( '<div></div>' );
|
||||
syncContainer = widgetContainer.find( '.widget-content:first' );
|
||||
syncContainer.before( fieldContainer );
|
||||
|
||||
widgetControl = new component.TextWidgetControl({
|
||||
el: widgetContainer
|
||||
el: fieldContainer,
|
||||
syncContainer: syncContainer
|
||||
});
|
||||
|
||||
component.widgetControls[ widgetId ] = widgetControl;
|
||||
@@ -256,6 +411,40 @@ wp.textWidgets = ( function( $ ) {
|
||||
renderWhenAnimationDone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup widget in accessibility mode.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
component.setupAccessibleMode = function setupAccessibleMode() {
|
||||
var widgetForm, idBase, widgetControl, fieldContainer, syncContainer;
|
||||
widgetForm = $( '.editwidget > form' );
|
||||
if ( 0 === widgetForm.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
idBase = widgetForm.find( '> .widget-control-actions > .id_base' ).val();
|
||||
if ( 'text' !== idBase ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bypass using TinyMCE when widget is in legacy mode.
|
||||
if ( ! widgetForm.find( '.visual' ).val() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fieldContainer = $( '<div></div>' );
|
||||
syncContainer = widgetForm.find( '> .widget-inside' );
|
||||
syncContainer.before( fieldContainer );
|
||||
|
||||
widgetControl = new component.TextWidgetControl({
|
||||
el: fieldContainer,
|
||||
syncContainer: syncContainer
|
||||
});
|
||||
|
||||
widgetControl.initializeEditor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync widget instance data sanitized from server back onto widget model.
|
||||
*
|
||||
@@ -319,6 +508,11 @@ wp.textWidgets = ( function( $ ) {
|
||||
var widgetContainer = $( this );
|
||||
component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer );
|
||||
});
|
||||
|
||||
// Accessibility mode.
|
||||
$( window ).on( 'load', function() {
|
||||
component.setupAccessibleMode();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
2
wp-admin/js/widgets/text-widgets.min.js
vendored
2
wp-admin/js/widgets/text-widgets.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -95,7 +95,7 @@ class WP_oEmbed {
|
||||
'#https?://kck\.st/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ),
|
||||
'#https?://cloudup\.com/.*#i' => array( 'https://cloudup.com/oembed', true ),
|
||||
'#https?://(www\.)?reverbnation\.com/.*#i' => array( 'https://www.reverbnation.com/oembed', true ),
|
||||
'#https?://videopress\.com/v/.*#' => array( 'https://public-api.wordpress.com/oembed/1.0/?for=' . $host, true ),
|
||||
'#https?://videopress\.com/v/.*#' => array( 'https://public-api.wordpress.com/oembed/?for=' . $host, true ),
|
||||
'#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#i' => array( 'https://www.reddit.com/oembed', true ),
|
||||
'#https?://(www\.)?speakerdeck\.com/.*#i' => array( 'https://speakerdeck.com/oembed.{format}', true ),
|
||||
'#https?://www\.facebook\.com/.*/posts/.*#i' => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
|
||||
|
||||
@@ -35,9 +35,23 @@ final class WP_Customize_Widgets {
|
||||
* @var array
|
||||
*/
|
||||
protected $core_widget_id_bases = array(
|
||||
'archives', 'calendar', 'categories', 'links', 'meta',
|
||||
'nav_menu', 'pages', 'recent-comments', 'recent-posts',
|
||||
'rss', 'search', 'tag_cloud', 'text',
|
||||
'archives',
|
||||
'calendar',
|
||||
'categories',
|
||||
'custom_html',
|
||||
'links',
|
||||
'media_audio',
|
||||
'media_image',
|
||||
'media_video',
|
||||
'meta',
|
||||
'nav_menu',
|
||||
'pages',
|
||||
'recent-comments',
|
||||
'recent-posts',
|
||||
'rss',
|
||||
'search',
|
||||
'tag_cloud',
|
||||
'text',
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -819,40 +819,45 @@ final class _WP_Editors {
|
||||
*
|
||||
*/
|
||||
public static function print_default_editor_scripts() {
|
||||
$settings = self::default_settings();
|
||||
$user_can_richedit = user_can_richedit();
|
||||
|
||||
$settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
|
||||
$settings['wpautop'] = false;
|
||||
$settings['indent'] = true;
|
||||
$settings['elementpath'] = false;
|
||||
if ( $user_can_richedit ) {
|
||||
$settings = self::default_settings();
|
||||
|
||||
// In production all plugins are loaded (they are in wp-editor.js.gz)
|
||||
// but only these will be initialized by default.
|
||||
$settings['plugins'] = implode( ',', array(
|
||||
'charmap',
|
||||
'colorpicker',
|
||||
'hr',
|
||||
'lists',
|
||||
// 'media',
|
||||
'paste',
|
||||
'tabfocus',
|
||||
'textcolor',
|
||||
'fullscreen',
|
||||
'wordpress',
|
||||
'wpautoresize',
|
||||
'wpeditimage',
|
||||
'wpemoji',
|
||||
'wpgallery',
|
||||
'wplink',
|
||||
// 'wpdialogs',
|
||||
'wptextpattern',
|
||||
// 'wpview',
|
||||
) );
|
||||
$settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
|
||||
$settings['wpautop'] = false;
|
||||
$settings['indent'] = true;
|
||||
$settings['elementpath'] = false;
|
||||
|
||||
$settings = self::_parse_init( $settings );
|
||||
if ( is_rtl() ) {
|
||||
$settings['directionality'] = 'rtl';
|
||||
}
|
||||
|
||||
$suffix = SCRIPT_DEBUG ? '' : '.min';
|
||||
$baseurl = self::get_baseurl();
|
||||
// In production all plugins are loaded (they are in wp-editor.js.gz).
|
||||
// The 'wpview', 'wpdialogs', and 'media' TinyMCE plugins are not initialized by default.
|
||||
// Can be added from js by using the 'wp-before-tinymce-init' event.
|
||||
$settings['plugins'] = implode( ',', array(
|
||||
'charmap',
|
||||
'colorpicker',
|
||||
'hr',
|
||||
'lists',
|
||||
'paste',
|
||||
'tabfocus',
|
||||
'textcolor',
|
||||
'fullscreen',
|
||||
'wordpress',
|
||||
'wpautoresize',
|
||||
'wpeditimage',
|
||||
'wpemoji',
|
||||
'wpgallery',
|
||||
'wplink',
|
||||
'wptextpattern',
|
||||
) );
|
||||
|
||||
$settings = self::_parse_init( $settings );
|
||||
} else {
|
||||
$settings = '{}';
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
@@ -867,17 +872,29 @@ final class _WP_Editors {
|
||||
};
|
||||
};
|
||||
|
||||
var tinyMCEPreInit = {
|
||||
baseURL: "<?php echo $baseurl; ?>",
|
||||
suffix: "<?php echo $suffix; ?>",
|
||||
mceInit: {},
|
||||
qtInit: {},
|
||||
load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
|
||||
};
|
||||
<?php
|
||||
|
||||
if ( $user_can_richedit ) {
|
||||
$suffix = SCRIPT_DEBUG ? '' : '.min';
|
||||
$baseurl = self::get_baseurl();
|
||||
|
||||
?>
|
||||
var tinyMCEPreInit = {
|
||||
baseURL: "<?php echo $baseurl; ?>",
|
||||
suffix: "<?php echo $suffix; ?>",
|
||||
mceInit: {},
|
||||
qtInit: {},
|
||||
load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
|
||||
};
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</script>
|
||||
<?php
|
||||
|
||||
self::print_tinymce_scripts();
|
||||
if ( $user_can_richedit ) {
|
||||
self::print_tinymce_scripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when the editor scripts are loaded for later initialization,
|
||||
|
||||
@@ -153,12 +153,13 @@ final class WP_oEmbed_Controller {
|
||||
*
|
||||
* @see WP_oEmbed::get_html()
|
||||
* @param WP_REST_Request $request Full data about the request.
|
||||
* @return WP_Error|array oEmbed response data or WP_Error on failure.
|
||||
* @return object|WP_Error oEmbed response data or WP_Error on failure.
|
||||
*/
|
||||
public function get_proxy_item( $request ) {
|
||||
$args = $request->get_params();
|
||||
|
||||
// Serve oEmbed data from cache if set.
|
||||
unset( $args['_wpnonce'] );
|
||||
$cache_key = 'oembed_' . md5( serialize( $args ) );
|
||||
$data = get_transient( $cache_key );
|
||||
if ( ! empty( $data ) ) {
|
||||
@@ -168,6 +169,14 @@ final class WP_oEmbed_Controller {
|
||||
$url = $request['url'];
|
||||
unset( $args['url'] );
|
||||
|
||||
// Copy maxwidth/maxheight to width/height since WP_oEmbed::fetch() uses these arg names.
|
||||
if ( isset( $args['maxwidth'] ) ) {
|
||||
$args['width'] = $args['maxwidth'];
|
||||
}
|
||||
if ( isset( $args['maxheight'] ) ) {
|
||||
$args['height'] = $args['maxheight'];
|
||||
}
|
||||
|
||||
$data = _wp_oembed_get_object()->get_data( $url, $args );
|
||||
|
||||
if ( false === $data ) {
|
||||
|
||||
@@ -60,3 +60,155 @@ require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-tag-cloud.php' );
|
||||
|
||||
/** WP_Nav_Menu_Widget class */
|
||||
require_once( ABSPATH . WPINC . '/widgets/class-wp-nav-menu-widget.php' );
|
||||
|
||||
/**
|
||||
* Core class used to implement a Custom HTML widget.
|
||||
*
|
||||
* Note that this class is only located in this file in the 4.8 branch
|
||||
* for the sake of automatic updates. In 4.9 and above, it is located at
|
||||
* `wp-includes/widgets/class-wp-widget-custom-html.php`.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @see WP_Widget
|
||||
*/
|
||||
class WP_Widget_Custom_HTML extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Default instance.
|
||||
*
|
||||
* @since 4.8.1
|
||||
* @var array
|
||||
*/
|
||||
protected $default_instance = array(
|
||||
'title' => '',
|
||||
'content' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets up a new Custom HTML widget instance.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*/
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget_custom_html',
|
||||
'description' => __( 'Arbitrary HTML code.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
);
|
||||
$control_ops = array(
|
||||
'width' => 400,
|
||||
'height' => 350,
|
||||
);
|
||||
parent::__construct( 'custom_html', __( 'Custom HTML' ), $widget_ops, $control_ops );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the content for the current Custom HTML widget instance.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param array $args Display arguments including 'before_title', 'after_title',
|
||||
* 'before_widget', and 'after_widget'.
|
||||
* @param array $instance Settings for the current Custom HTML widget instance.
|
||||
*/
|
||||
public function widget( $args, $instance ) {
|
||||
|
||||
$instance = array_merge( $this->default_instance, $instance );
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
|
||||
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
|
||||
|
||||
// Prepare instance data that looks like a normal Text widget.
|
||||
$simulated_text_widget_instance = array_merge( $instance, array(
|
||||
'text' => isset( $instance['content'] ) ? $instance['content'] : '',
|
||||
'filter' => false, // Because wpautop is not applied.
|
||||
'visual' => false, // Because it wasn't created in TinyMCE.
|
||||
) );
|
||||
unset( $simulated_text_widget_instance['content'] ); // Was moved to 'text' prop.
|
||||
|
||||
/** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */
|
||||
$content = apply_filters( 'widget_text', $instance['content'], $simulated_text_widget_instance, $this );
|
||||
|
||||
/**
|
||||
* Filters the content of the Custom HTML widget.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param string $content The widget content.
|
||||
* @param array $instance Array of settings for the current widget.
|
||||
* @param WP_Widget_Custom_HTML $this Current Custom HTML widget instance.
|
||||
*/
|
||||
$content = apply_filters( 'widget_custom_html_content', $content, $instance, $this );
|
||||
|
||||
// Inject the Text widget's container class name alongside this widget's class name for theme styling compatibility.
|
||||
$args['before_widget'] = preg_replace( '/(?<=\sclass=["\'])/', 'widget_text ', $args['before_widget'] );
|
||||
|
||||
echo $args['before_widget'];
|
||||
if ( ! empty( $title ) ) {
|
||||
echo $args['before_title'] . $title . $args['after_title'];
|
||||
}
|
||||
echo '<div class="textwidget custom-html-widget">'; // The textwidget class is for theme styling compatibility.
|
||||
echo $content;
|
||||
echo '</div>';
|
||||
echo $args['after_widget'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating settings for the current Custom HTML widget instance.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param array $new_instance New settings for this instance as input by the user via
|
||||
* WP_Widget::form().
|
||||
* @param array $old_instance Old settings for this instance.
|
||||
* @return array Settings to save or bool false to cancel saving.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$instance = array_merge( $this->default_instance, $old_instance );
|
||||
$instance['title'] = sanitize_text_field( $new_instance['title'] );
|
||||
if ( current_user_can( 'unfiltered_html' ) ) {
|
||||
$instance['content'] = $new_instance['content'];
|
||||
} else {
|
||||
$instance['content'] = wp_kses_post( $new_instance['content'] );
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the Custom HTML widget settings form.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param array $instance Current instance.
|
||||
* @returns void
|
||||
*/
|
||||
public function form( $instance ) {
|
||||
$instance = wp_parse_args( (array) $instance, $this->default_instance );
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id( 'content' ); ?>"><?php _e( 'Content:' ); ?></label>
|
||||
<textarea class="widefat code" rows="16" cols="20" id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>"><?php echo esc_textarea( $instance['content'] ); ?></textarea>
|
||||
</p>
|
||||
|
||||
<?php if ( ! current_user_can( 'unfiltered_html' ) ) : ?>
|
||||
<?php
|
||||
$probably_unsafe_html = array( 'script', 'iframe', 'form', 'input', 'style' );
|
||||
$allowed_html = wp_kses_allowed_html( 'post' );
|
||||
$disallowed_html = array_diff( $probably_unsafe_html, array_keys( $allowed_html ) );
|
||||
?>
|
||||
<?php if ( ! empty( $disallowed_html ) ) : ?>
|
||||
<p>
|
||||
<?php _e( 'Some HTML tags are not permitted, including:' ); ?>
|
||||
<code><?php echo join( '</code>, <code>', $disallowed_html ); ?></code>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8227,13 +8227,13 @@ UploaderInline = View.extend({
|
||||
},
|
||||
show: function() {
|
||||
this.$el.removeClass( 'hidden' );
|
||||
if ( this.controller.$uploaderToggler.length ) {
|
||||
if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
|
||||
this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' );
|
||||
}
|
||||
},
|
||||
hide: function() {
|
||||
this.$el.addClass( 'hidden' );
|
||||
if ( this.controller.$uploaderToggler.length ) {
|
||||
if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
|
||||
this.controller.$uploaderToggler
|
||||
.attr( 'aria-expanded', 'false' )
|
||||
// Move focus back to the toggle button when closing the uploader.
|
||||
|
||||
2
wp-includes/js/media-views.min.js
vendored
2
wp-includes/js/media-views.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,7 +1,9 @@
|
||||
/* global getUserSetting, setUserSetting */
|
||||
( function( tinymce ) {
|
||||
// Set the minimum value for the modals z-index higher than #wpadminbar (100000)
|
||||
tinymce.ui.FloatPanel.zIndex = 100100;
|
||||
if ( tinymce.ui.FloatPanel.zIndex < 100100 ) {
|
||||
tinymce.ui.FloatPanel.zIndex = 100100;
|
||||
}
|
||||
|
||||
tinymce.PluginManager.add( 'wordpress', function( editor ) {
|
||||
var wpAdvButton, style,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -101,8 +101,9 @@ img {
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.wp-caption img {
|
||||
display: block;
|
||||
dl.wp-caption dt.wp-caption-dt img {
|
||||
display: inline-block;
|
||||
margin-bottom: -1ex;
|
||||
}
|
||||
|
||||
div.mceTemp {
|
||||
@@ -180,6 +181,14 @@ img[data-mce-selected]::selection {
|
||||
background: #bfe6ff;
|
||||
}
|
||||
|
||||
.mce-content-body .wp-caption-dt a[data-mce-selected] {
|
||||
outline: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.mce-content-body code {
|
||||
padding: 2px 4px;
|
||||
margin: 0;
|
||||
|
||||
Binary file not shown.
@@ -1112,7 +1112,10 @@
|
||||
'PostsRevisions': 'PostRevisions',
|
||||
'PostsTags': 'PostTags'
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
modelEndpoints = routeModel.get( 'modelEndpoints' ),
|
||||
modelRegex = new RegExp( '(?:.*[+)]|\/(' + modelEndpoints.join( '|' ) + '))$' );
|
||||
|
||||
/**
|
||||
* Iterate thru the routes, picking up models and collections to build. Builds two arrays,
|
||||
@@ -1137,8 +1140,8 @@
|
||||
index !== ( '/' + routeModel.get( 'versionString' ).slice( 0, -1 ) )
|
||||
) {
|
||||
|
||||
// Single items end with a regex (or the special case 'me').
|
||||
if ( /(?:.*[+)]|\/me)$/.test( index ) ) {
|
||||
// Single items end with a regex, or a special case word.
|
||||
if ( modelRegex.test( index ) ) {
|
||||
modelRoutes.push( { index: index, route: route } );
|
||||
} else {
|
||||
|
||||
@@ -1360,10 +1363,11 @@
|
||||
wp.api.init = function( args ) {
|
||||
var endpoint, attributes = {}, deferred, promise;
|
||||
|
||||
args = args || {};
|
||||
attributes.apiRoot = args.apiRoot || wpApiSettings.root || '/wp-json';
|
||||
attributes.versionString = args.versionString || wpApiSettings.versionString || 'wp/v2/';
|
||||
attributes.schema = args.schema || null;
|
||||
args = args || {};
|
||||
attributes.apiRoot = args.apiRoot || wpApiSettings.root || '/wp-json';
|
||||
attributes.versionString = args.versionString || wpApiSettings.versionString || 'wp/v2/';
|
||||
attributes.schema = args.schema || null;
|
||||
attributes.modelEndpoints = args.modelEndpoints || [ 'me', 'settings' ];
|
||||
if ( ! attributes.schema && attributes.apiRoot === wpApiSettings.root && attributes.versionString === wpApiSettings.versionString ) {
|
||||
attributes.schema = wpApiSettings.schema;
|
||||
}
|
||||
|
||||
2
wp-includes/js/wp-api.min.js
vendored
2
wp-includes/js/wp-api.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -2556,7 +2556,11 @@ function wp_video_shortcode( $attr, $content = '' ) {
|
||||
} elseif ( $is_vimeo ) {
|
||||
// Remove all query arguments and force SSL - see #40866.
|
||||
$parsed_vimeo_url = wp_parse_url( $atts['src'] );
|
||||
$atts['src'] = 'https://' . $parsed_vimeo_url['host'] . $parsed_vimeo_url['path'];
|
||||
$vimeo_src = 'https://' . $parsed_vimeo_url['host'] . $parsed_vimeo_url['path'];
|
||||
|
||||
// Add loop param for mejs bug - see #40977, not needed after #39686.
|
||||
$loop = $atts['loop'] ? '1' : '0';
|
||||
$atts['src'] = add_query_arg( 'loop', $loop, $vimeo_src );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,11 @@ function get_rest_url( $blog_id = null, $path = '/', $scheme = 'rest' ) {
|
||||
$url .= '/' . ltrim( $path, '/' );
|
||||
} else {
|
||||
$url = trailingslashit( get_home_url( $blog_id, '', $scheme ) );
|
||||
// nginx only allows HTTP/1.0 methods when redirecting from / to /index.php
|
||||
// To work around this, we manually add index.php to the URL, avoiding the redirect.
|
||||
if ( 'index.php' !== substr( $url, 9 ) ) {
|
||||
$url .= 'index.php';
|
||||
}
|
||||
|
||||
$path = '/' . ltrim( $path, '/' );
|
||||
|
||||
|
||||
@@ -387,6 +387,20 @@ class WP_REST_Server {
|
||||
// Embed links inside the request.
|
||||
$result = $this->response_to_data( $result, isset( $_GET['_embed'] ) );
|
||||
|
||||
/**
|
||||
* Filters the API response.
|
||||
*
|
||||
* Allows modification of the response data after inserting
|
||||
* embedded data (if any) and before echoing the response data.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param array $result Response data to send to the client.
|
||||
* @param WP_REST_Server $this Server instance.
|
||||
* @param WP_REST_Request $request Request used to generate the response.
|
||||
*/
|
||||
$result = apply_filters( 'rest_pre_echo_response', $result, $this, $request );
|
||||
|
||||
$result = wp_json_encode( $result );
|
||||
|
||||
$json_error_message = $this->get_json_last_error();
|
||||
|
||||
@@ -608,7 +608,7 @@ function wp_default_scripts( &$scripts ) {
|
||||
$scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) );
|
||||
$scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) );
|
||||
$scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) );
|
||||
$scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util' ) );
|
||||
$scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) );
|
||||
$scripts->add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' );
|
||||
|
||||
$scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y' ), false, 1 );
|
||||
@@ -845,7 +845,7 @@ function wp_default_styles( &$styles ) {
|
||||
$styles->add( 'themes', "/wp-admin/css/themes$suffix.css" );
|
||||
$styles->add( 'about', "/wp-admin/css/about$suffix.css" );
|
||||
$styles->add( 'nav-menus', "/wp-admin/css/nav-menus$suffix.css" );
|
||||
$styles->add( 'widgets', "/wp-admin/css/widgets$suffix.css" );
|
||||
$styles->add( 'widgets', "/wp-admin/css/widgets$suffix.css", array( 'wp-pointer' ) );
|
||||
$styles->add( 'site-icon', "/wp-admin/css/site-icon$suffix.css" );
|
||||
$styles->add( 'l10n', "/wp-admin/css/l10n$suffix.css" );
|
||||
|
||||
|
||||
@@ -1864,7 +1864,10 @@ function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
|
||||
$args['taxonomy'] = $taxonomies;
|
||||
$args['object_ids'] = $object_ids;
|
||||
|
||||
$terms = array_merge( $terms, get_terms( $args ) );
|
||||
// Taxonomies registered without an 'args' param are handled here.
|
||||
if ( ! empty( $taxonomies ) ) {
|
||||
$terms = array_merge( $terms, get_terms( $args ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the terms for a given object or objects.
|
||||
|
||||
@@ -1948,15 +1948,19 @@ function get_theme_starter_content() {
|
||||
'text_business_info' => array( 'text', array(
|
||||
'title' => _x( 'Find Us', 'Theme starter content' ),
|
||||
'text' => join( '', array(
|
||||
'<p><strong>' . _x( 'Address', 'Theme starter content' ) . '</strong><br />',
|
||||
_x( '123 Main Street', 'Theme starter content' ) . '<br />' . _x( 'New York, NY 10001', 'Theme starter content' ) . '</p>',
|
||||
'<p><strong>' . _x( 'Hours', 'Theme starter content' ) . '</strong><br />',
|
||||
_x( 'Monday—Friday: 9:00AM–5:00PM', 'Theme starter content' ) . '<br />' . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' ) . '</p>'
|
||||
'<strong>' . _x( 'Address', 'Theme starter content' ) . "</strong>\n",
|
||||
_x( '123 Main Street', 'Theme starter content' ) . "\n" . _x( 'New York, NY 10001', 'Theme starter content' ) . "\n\n",
|
||||
'<strong>' . _x( 'Hours', 'Theme starter content' ) . "</strong>\n",
|
||||
_x( 'Monday—Friday: 9:00AM–5:00PM', 'Theme starter content' ) . "\n" . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' )
|
||||
) ),
|
||||
'filter' => true,
|
||||
'visual' => true,
|
||||
) ),
|
||||
'text_about' => array( 'text', array(
|
||||
'title' => _x( 'About This Site', 'Theme starter content' ),
|
||||
'text' => _x( 'This may be a good place to introduce yourself and your site or include some credits.', 'Theme starter content' ),
|
||||
'filter' => true,
|
||||
'visual' => true,
|
||||
) ),
|
||||
'archives' => array( 'archives', array(
|
||||
'title' => _x( 'Archives', 'Theme starter content' ),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '4.8';
|
||||
$wp_version = '4.8.1';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
||||
@@ -1474,6 +1474,8 @@ function wp_widgets_init() {
|
||||
|
||||
register_widget( 'WP_Nav_Menu_Widget' );
|
||||
|
||||
register_widget( 'WP_Widget_Custom_HTML' );
|
||||
|
||||
/**
|
||||
* Fires after all default WordPress widgets have been registered.
|
||||
*
|
||||
|
||||
@@ -115,13 +115,9 @@ class WP_Widget_Media_Video extends WP_Widget_Media {
|
||||
$attachment = get_post( $instance['attachment_id'] );
|
||||
}
|
||||
|
||||
$src = $instance['url'];
|
||||
if ( $attachment ) {
|
||||
$src = wp_get_attachment_url( $attachment->ID );
|
||||
} else {
|
||||
|
||||
// Manually add the loop query argument.
|
||||
$loop = $instance['loop'] ? '1' : '0';
|
||||
$src = empty( $instance['url'] ) ? $instance['url'] : add_query_arg( 'loop', $loop, $instance['url'] );
|
||||
}
|
||||
|
||||
if ( empty( $src ) ) {
|
||||
|
||||
@@ -33,6 +33,14 @@ abstract class WP_Widget_Media extends WP_Widget {
|
||||
'add_media' => '',
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether or not the widget has been registered yet.
|
||||
*
|
||||
* @since 4.8.1
|
||||
* @var bool
|
||||
*/
|
||||
protected $registered = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -86,8 +94,16 @@ abstract class WP_Widget_Media extends WP_Widget {
|
||||
*
|
||||
* @since 4.8.0
|
||||
* @access public
|
||||
*
|
||||
* @param integer $number Optional. The unique order number of this widget instance
|
||||
* compared to other instances of the same class. Default -1.
|
||||
*/
|
||||
public function _register() {
|
||||
public function _register_one( $number = -1 ) {
|
||||
parent::_register_one( $number );
|
||||
if ( $this->registered ) {
|
||||
return;
|
||||
}
|
||||
$this->registered = true;
|
||||
|
||||
// Note that the widgets component in the customizer will also do the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts().
|
||||
add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) );
|
||||
@@ -100,8 +116,6 @@ abstract class WP_Widget_Media extends WP_Widget {
|
||||
add_action( 'admin_footer-widgets.php', array( $this, 'render_control_template_scripts' ) );
|
||||
|
||||
add_filter( 'display_media_states', array( $this, 'display_media_state' ), 10, 2 );
|
||||
|
||||
parent::_register();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
*/
|
||||
class WP_Widget_Text extends WP_Widget {
|
||||
|
||||
/**
|
||||
* Whether or not the widget has been registered yet.
|
||||
*
|
||||
* @since 4.8.1
|
||||
* @var bool
|
||||
*/
|
||||
protected $registered = false;
|
||||
|
||||
/**
|
||||
* Sets up a new Text widget instance.
|
||||
*
|
||||
@@ -25,7 +33,7 @@ class WP_Widget_Text extends WP_Widget {
|
||||
public function __construct() {
|
||||
$widget_ops = array(
|
||||
'classname' => 'widget_text',
|
||||
'description' => __( 'Arbitrary text or HTML.' ),
|
||||
'description' => __( 'Arbitrary text.' ),
|
||||
'customize_selective_refresh' => true,
|
||||
);
|
||||
$control_ops = array(
|
||||
@@ -38,18 +46,135 @@ class WP_Widget_Text extends WP_Widget {
|
||||
/**
|
||||
* Add hooks for enqueueing assets when registering all widget instances of this widget class.
|
||||
*
|
||||
* @since 4.8.0
|
||||
* @access public
|
||||
* @param integer $number Optional. The unique order number of this widget instance
|
||||
* compared to other instances of the same class. Default -1.
|
||||
*/
|
||||
public function _register() {
|
||||
public function _register_one( $number = -1 ) {
|
||||
parent::_register_one( $number );
|
||||
if ( $this->registered ) {
|
||||
return;
|
||||
}
|
||||
$this->registered = true;
|
||||
|
||||
// Note that the widgets component in the customizer will also do the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts().
|
||||
add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) );
|
||||
|
||||
// Note that the widgets component in the customizer will also do the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts().
|
||||
add_action( 'admin_footer-widgets.php', array( $this, 'render_control_template_scripts' ) );
|
||||
}
|
||||
|
||||
parent::_register();
|
||||
/**
|
||||
* Determines whether a given instance is legacy and should bypass using TinyMCE.
|
||||
*
|
||||
* @since 4.8.1
|
||||
*
|
||||
* @param array $instance {
|
||||
* Instance data.
|
||||
*
|
||||
* @type string $text Content.
|
||||
* @type bool|string $filter Whether autop or content filters should apply.
|
||||
* @type bool $legacy Whether widget is in legacy mode.
|
||||
* }
|
||||
* @return bool Whether Text widget instance contains legacy data.
|
||||
*/
|
||||
public function is_legacy_instance( $instance ) {
|
||||
|
||||
// Legacy mode when not in visual mode.
|
||||
if ( isset( $instance['visual'] ) ) {
|
||||
return ! $instance['visual'];
|
||||
}
|
||||
|
||||
// Or, the widget has been added/updated in 4.8.0 then filter prop is 'content' and it is no longer legacy.
|
||||
if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the text is empty, then nothing is preventing migration to TinyMCE.
|
||||
if ( empty( $instance['text'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$wpautop = ! empty( $instance['filter'] );
|
||||
$has_line_breaks = ( false !== strpos( trim( $instance['text'] ), "\n" ) );
|
||||
|
||||
// If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode.
|
||||
if ( ! $wpautop && $has_line_breaks ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If an HTML comment is present, assume legacy mode.
|
||||
if ( false !== strpos( $instance['text'], '<!--' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the rare case that DOMDocument is not available we cannot reliably sniff content and so we assume legacy.
|
||||
if ( ! class_exists( 'DOMDocument' ) ) {
|
||||
// @codeCoverageIgnoreStart
|
||||
return true;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML( sprintf(
|
||||
'<html><head><meta charset="%s"></head><body>%s</body></html>',
|
||||
esc_attr( get_bloginfo( 'charset' ) ),
|
||||
$instance['text']
|
||||
) );
|
||||
$body = $doc->getElementsByTagName( 'body' )->item( 0 );
|
||||
|
||||
// See $allowedposttags.
|
||||
$safe_elements_attributes = array(
|
||||
'strong' => array(),
|
||||
'em' => array(),
|
||||
'b' => array(),
|
||||
'i' => array(),
|
||||
'u' => array(),
|
||||
's' => array(),
|
||||
'ul' => array(),
|
||||
'ol' => array(),
|
||||
'li' => array(),
|
||||
'hr' => array(),
|
||||
'abbr' => array(),
|
||||
'acronym' => array(),
|
||||
'code' => array(),
|
||||
'dfn' => array(),
|
||||
'a' => array(
|
||||
'href' => true,
|
||||
),
|
||||
'img' => array(
|
||||
'src' => true,
|
||||
'alt' => true,
|
||||
),
|
||||
);
|
||||
$safe_empty_elements = array( 'img', 'hr', 'iframe' );
|
||||
|
||||
foreach ( $body->getElementsByTagName( '*' ) as $element ) {
|
||||
/** @var DOMElement $element */
|
||||
$tag_name = strtolower( $element->nodeName );
|
||||
|
||||
// If the element is not safe, then the instance is legacy.
|
||||
if ( ! isset( $safe_elements_attributes[ $tag_name ] ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the element is not safely empty and it has empty contents, then legacy mode.
|
||||
if ( ! in_array( $tag_name, $safe_empty_elements, true ) && '' === trim( $element->textContent ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If an attribute is not recognized as safe, then the instance is legacy.
|
||||
foreach ( $element->attributes as $attribute ) {
|
||||
/** @var DOMAttr $attribute */
|
||||
$attribute_name = strtolower( $attribute->nodeName );
|
||||
|
||||
if ( ! isset( $safe_elements_attributes[ $tag_name ][ $attribute_name ] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the text contains no elements/attributes that TinyMCE could drop, and therefore the widget does not need legacy mode.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,38 +193,66 @@ class WP_Widget_Text extends WP_Widget {
|
||||
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
|
||||
|
||||
$text = ! empty( $instance['text'] ) ? $instance['text'] : '';
|
||||
$is_visual_text_widget = ( ! empty( $instance['visual'] ) && ! empty( $instance['filter'] ) );
|
||||
|
||||
// In 4.8.0 only, visual Text widgets get filter=content, without visual prop; upgrade instance props just-in-time.
|
||||
if ( ! $is_visual_text_widget ) {
|
||||
$is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] );
|
||||
}
|
||||
if ( $is_visual_text_widget ) {
|
||||
$instance['filter'] = true;
|
||||
$instance['visual'] = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Just-in-time temporarily upgrade Visual Text widget shortcode handling
|
||||
* (with support added by plugin) from the widget_text filter to
|
||||
* widget_text_content:11 to prevent wpautop from corrupting HTML output
|
||||
* added by the shortcode.
|
||||
*/
|
||||
$widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' );
|
||||
$should_upgrade_shortcode_handling = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority );
|
||||
if ( $should_upgrade_shortcode_handling ) {
|
||||
remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority );
|
||||
add_filter( 'widget_text_content', 'do_shortcode', 11 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the content of the Text widget.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @since 4.4.0 Added the `$this` parameter.
|
||||
* @since 4.8.1 The `$this` param may now be a `WP_Widget_Custom_HTML` object in addition to a `WP_Widget_Text` object.
|
||||
*
|
||||
* @param string $text The widget content.
|
||||
* @param array $instance Array of settings for the current widget.
|
||||
* @param WP_Widget_Text $this Current Text widget instance.
|
||||
* @param string $text The widget content.
|
||||
* @param array $instance Array of settings for the current widget.
|
||||
* @param WP_Widget_Text|WP_Widget_Custom_HTML $this Current Text widget instance.
|
||||
*/
|
||||
$text = apply_filters( 'widget_text', $text, $instance, $this );
|
||||
|
||||
if ( isset( $instance['filter'] ) ) {
|
||||
if ( 'content' === $instance['filter'] ) {
|
||||
if ( $is_visual_text_widget ) {
|
||||
|
||||
/**
|
||||
* Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor.
|
||||
*
|
||||
* By default a subset of the_content filters are applied, including wpautop and wptexturize.
|
||||
*
|
||||
* @since 4.8.0
|
||||
*
|
||||
* @param string $text The widget content.
|
||||
* @param array $instance Array of settings for the current widget.
|
||||
* @param WP_Widget_Text $this Current Text widget instance.
|
||||
*/
|
||||
$text = apply_filters( 'widget_text_content', $text, $instance, $this );
|
||||
/**
|
||||
* Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor.
|
||||
*
|
||||
* By default a subset of the_content filters are applied, including wpautop and wptexturize.
|
||||
*
|
||||
* @since 4.8.0
|
||||
*
|
||||
* @param string $text The widget content.
|
||||
* @param array $instance Array of settings for the current widget.
|
||||
* @param WP_Widget_Text $this Current Text widget instance.
|
||||
*/
|
||||
$text = apply_filters( 'widget_text_content', $text, $instance, $this );
|
||||
|
||||
} elseif ( $instance['filter'] ) {
|
||||
$text = wpautop( $text ); // Back-compat for instances prior to 4.8.
|
||||
}
|
||||
} elseif ( ! empty( $instance['filter'] ) ) {
|
||||
$text = wpautop( $text ); // Back-compat for instances prior to 4.8.
|
||||
}
|
||||
|
||||
// Undo temporary upgrade of the plugin-supplied shortcode handling.
|
||||
if ( $should_upgrade_shortcode_handling ) {
|
||||
remove_filter( 'widget_text_content', 'do_shortcode', 11 );
|
||||
add_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority );
|
||||
}
|
||||
|
||||
echo $args['before_widget'];
|
||||
@@ -125,7 +278,15 @@ class WP_Widget_Text extends WP_Widget {
|
||||
* @return array Settings to save or bool false to cancel saving.
|
||||
*/
|
||||
public function update( $new_instance, $old_instance ) {
|
||||
$new_instance = wp_parse_args( $new_instance, array(
|
||||
'title' => '',
|
||||
'text' => '',
|
||||
'filter' => false, // For back-compat.
|
||||
'visual' => null, // Must be explicitly defined.
|
||||
) );
|
||||
|
||||
$instance = $old_instance;
|
||||
|
||||
$instance['title'] = sanitize_text_field( $new_instance['title'] );
|
||||
if ( current_user_can( 'unfiltered_html' ) ) {
|
||||
$instance['text'] = $new_instance['text'];
|
||||
@@ -133,13 +294,24 @@ class WP_Widget_Text extends WP_Widget {
|
||||
$instance['text'] = wp_kses_post( $new_instance['text'] );
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply.
|
||||
* Prior to 4.8, this is a boolean value used to indicate whether or not wpautop should be
|
||||
* applied. By re-using this property, downgrading WordPress from 4.8 to 4.7 will ensure
|
||||
* that the content for Text widgets created with TinyMCE will continue to get wpautop.
|
||||
*/
|
||||
$instance['filter'] = 'content';
|
||||
$instance['filter'] = ! empty( $new_instance['filter'] );
|
||||
|
||||
// Upgrade 4.8.0 format.
|
||||
if ( isset( $old_instance['filter'] ) && 'content' === $old_instance['filter'] ) {
|
||||
$instance['visual'] = true;
|
||||
}
|
||||
if ( 'content' === $new_instance['filter'] ) {
|
||||
$instance['visual'] = true;
|
||||
}
|
||||
|
||||
if ( isset( $new_instance['visual'] ) ) {
|
||||
$instance['visual'] = ! empty( $new_instance['visual'] );
|
||||
}
|
||||
|
||||
// Filter is always true in visual mode.
|
||||
if ( ! empty( $instance['visual'] ) ) {
|
||||
$instance['filter'] = true;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
@@ -160,6 +332,7 @@ class WP_Widget_Text extends WP_Widget {
|
||||
*
|
||||
* @since 2.8.0
|
||||
* @since 4.8.0 Form only contains hidden inputs which are synced with JS template.
|
||||
* @since 4.8.1 Restored original form to be displayed when in legacy mode.
|
||||
* @access public
|
||||
* @see WP_Widget_Visual_Text::render_control_template_scripts()
|
||||
*
|
||||
@@ -175,9 +348,33 @@ class WP_Widget_Text extends WP_Widget {
|
||||
)
|
||||
);
|
||||
?>
|
||||
<input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>">
|
||||
<input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>">
|
||||
<?php if ( ! $this->is_legacy_instance( $instance ) ) : ?>
|
||||
<input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>">
|
||||
<input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>">
|
||||
<input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" class="filter" type="hidden" value="on">
|
||||
<input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual" type="hidden" value="on">
|
||||
<?php else : ?>
|
||||
<input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual" type="hidden" value="">
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
|
||||
</p>
|
||||
<div class="notice inline notice-info notice-alt">
|
||||
<?php if ( ! isset( $instance['visual'] ) ) : ?>
|
||||
<p><?php _e( 'This widget may contain code that may work better in the new “Custom HTML” widget. How about trying that widget instead?' ); ?></p>
|
||||
<?php else : ?>
|
||||
<p><?php _e( 'This widget may have contained code that may work better in the new “Custom HTML” widget. If you haven’t yet, how about trying that widget instead?' ); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id( 'text' ); ?>"><?php _e( 'Content:' ); ?></label>
|
||||
<textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>"><?php echo esc_textarea( $instance['text'] ); ?></textarea>
|
||||
</p>
|
||||
<p>
|
||||
<input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" type="checkbox"<?php checked( ! empty( $instance['filter'] ) ); ?> /> <label for="<?php echo $this->get_field_id( 'filter' ); ?>"><?php _e( 'Automatically add paragraphs' ); ?></label>
|
||||
</p>
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,6 +384,7 @@ class WP_Widget_Text extends WP_Widget {
|
||||
* @access public
|
||||
*/
|
||||
public function render_control_template_scripts() {
|
||||
$dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
|
||||
?>
|
||||
<script type="text/html" id="tmpl-widget-text-control-fields">
|
||||
<# var elementIdPrefix = 'el' + String( Math.random() ).replace( /\D/g, '' ) + '_' #>
|
||||
@@ -194,6 +392,41 @@ class WP_Widget_Text extends WP_Widget {
|
||||
<label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:' ); ?></label>
|
||||
<input id="{{ elementIdPrefix }}title" type="text" class="widefat title">
|
||||
</p>
|
||||
|
||||
<?php if ( ! in_array( 'text_widget_custom_html', $dismissed_pointers, true ) ) : ?>
|
||||
<div hidden class="wp-pointer custom-html-widget-pointer wp-pointer-top">
|
||||
<div class="wp-pointer-content">
|
||||
<h3><?php _e( 'New Custom HTML Widget' ); ?></h3>
|
||||
<?php if ( is_customize_preview() ) : ?>
|
||||
<p><?php _e( 'Hey, did you hear we have a “Custom HTML” widget now? You can find it by pressing the “<a class="add-widget" href="#">Add a Widget</a>” button and searching for “HTML”. Check it out to add some custom code to your site!' ); ?></p>
|
||||
<?php else : ?>
|
||||
<p><?php _e( 'Hey, did you hear we have a “Custom HTML” widget now? You can find it by scanning the list of available widgets on this screen. Check it out to add some custom code to your site!' ); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="wp-pointer-buttons">
|
||||
<a class="close" href="#"><?php _e( 'Dismiss' ); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wp-pointer-arrow">
|
||||
<div class="wp-pointer-arrow-inner"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! in_array( 'text_widget_paste_html', $dismissed_pointers, true ) ) : ?>
|
||||
<div hidden class="wp-pointer paste-html-pointer wp-pointer-top">
|
||||
<div class="wp-pointer-content">
|
||||
<h3><?php _e( 'Did you just paste HTML?' ); ?></h3>
|
||||
<p><?php _e( 'Hey there, looks like you just pasted HTML into the “Visual” tab of the Text widget. You may want to paste your code into the “Text” tab instead. Alternately, try out the new “Custom HTML” widget!' ); ?></p>
|
||||
<div class="wp-pointer-buttons">
|
||||
<a class="close" href="#"><?php _e( 'Dismiss' ); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wp-pointer-arrow">
|
||||
<div class="wp-pointer-arrow-inner"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<p>
|
||||
<label for="{{ elementIdPrefix }}text" class="screen-reader-text"><?php esc_html_e( 'Content:' ); ?></label>
|
||||
<textarea id="{{ elementIdPrefix }}text" class="widefat text wp-editor-area" style="height: 200px" rows="16" cols="20"></textarea>
|
||||
|
||||
Reference in New Issue
Block a user