Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 59 additions & 26 deletions src/wp-includes/class-wp-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,8 @@ public static function parse_settings( $editor_id, $settings ) {
*/
public static function editor( $content, $editor_id, $settings = array() ) {
$set = self::parse_settings( $editor_id, $settings );
$editor_class = ' class="' . trim( esc_attr( $set['editor_class'] ) . ' wp-editor-area' ) . '"';
$tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
$default_editor = 'html';
$buttons = '';
$autocomplete = '';
$editor_id_attr = esc_attr( $editor_id );

if ( $set['drag_drop_upload'] ) {
self::$drag_drop_upload = true;
Expand All @@ -180,19 +176,23 @@ public static function editor( $content, $editor_id, $settings = array() ) {
}

if ( self::$this_tinymce ) {
$autocomplete = ' autocomplete="off"';

if ( self::$this_quicktags ) {
$default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
// 'html' is used for the "Text" editor tab.
if ( 'html' !== $default_editor ) {
$default_editor = 'tinymce';
}

$buttons .= '<button type="button" id="' . $editor_id_attr . '-tmce" class="wp-switch-editor switch-tmce"' .
' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Visual', 'Name for the Visual editor tab' ) . "</button>\n";
$buttons .= '<button type="button" id="' . $editor_id_attr . '-html" class="wp-switch-editor switch-html"' .
' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
$buttons .= WP_HTML::render( <<<HTML
<button type="button" id="</%id>-tmce" class="wp-switch-editor switch-tmce" data-wp-editor-id="</%id>"></%visual_label></button>
<button type="button" id="</%id>-html" class="wp-switch-editor switch-html" data-wp-editor-id="</%id>"></%text_label></button>
HTML,
array(
'id' => $editor_id,
'text_label' => _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ),
'visual_label' => _x( 'Visual', 'Name for the Visual editor tab' ),
)
);
} else {
$default_editor = 'tinymce';
}
Expand All @@ -201,11 +201,13 @@ public static function editor( $content, $editor_id, $settings = array() ) {
$switch_class = 'html' === $default_editor ? 'html-active' : 'tmce-active';
$wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;

if ( $set['_content_editor_dfw'] ) {
$wrap_class .= ' has-dfw';
}

echo '<div id="wp-' . $editor_id_attr . '-wrap" class="' . $wrap_class . '">';
echo WP_HTML::render(
'<div id="wp-</%id>-wrap" class="wp-core-jui wp-editor-wrap </%has_dfw>">',
array(
'id' => $editor_id,
'has_dfw' => $set['_content_editor_dfw'] ? 'has-dfw' : '',
)
);

if ( self::$editor_buttons_css ) {
wp_print_styles( 'editor-buttons' );
Expand All @@ -217,7 +219,10 @@ public static function editor( $content, $editor_id, $settings = array() ) {
}

if ( ! empty( $buttons ) || $set['media_buttons'] ) {
echo '<div id="wp-' . $editor_id_attr . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
echo WP_HTML::render(
'<div id="wp-</%id>-editor-tools" class="wp-editor-tools hide-if-no-js">',
array( 'id' => $editor_id )
);

if ( $set['media_buttons'] ) {
self::$has_medialib = true;
Expand All @@ -226,7 +231,10 @@ public static function editor( $content, $editor_id, $settings = array() ) {
require ABSPATH . 'wp-admin/includes/media.php';
}

echo '<div id="wp-' . $editor_id_attr . '-media-buttons" class="wp-media-buttons">';
echo WP_HTML::render(
'<div id="wp-</%id>-media-button" class="wp-media-buttons">',
array( 'id' => $editor_id )
);

/**
* Fires after the default media button(s) are displayed.
Expand All @@ -249,10 +257,13 @@ public static function editor( $content, $editor_id, $settings = array() ) {
if ( 'content' === $editor_id && ! empty( $GLOBALS['current_screen'] ) && 'post' === $GLOBALS['current_screen']->base ) {
$toolbar_id = 'ed_toolbar';
} else {
$toolbar_id = 'qt_' . $editor_id_attr . '_toolbar';
$toolbar_id = 'qt_' . $editor_id . '_toolbar';
}

$quicktags_toolbar = '<div id="' . $toolbar_id . '" class="quicktags-toolbar hide-if-no-js"></div>';
$quicktags_toolbar = WP_HTML::render(
'<div id="</%id>" class="quicktags-toolbar hide-if-no-js"></div>',
array( 'id' => $toolbar_id )
);
}

/**
Expand All @@ -264,10 +275,28 @@ public static function editor( $content, $editor_id, $settings = array() ) {
*/
$the_editor = apply_filters(
'the_editor',
'<div id="wp-' . $editor_id_attr . '-editor-container" class="wp-editor-container">' .
WP_HTML::render(
'<div id="wp-</%id>-editor-container" class="wp-editor-container">',
array( 'id' => $editor_id )
) .
$quicktags_toolbar .
'<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . esc_attr( $set['textarea_name'] ) . '" ' .
'id="' . $editor_id_attr . '">%s</textarea></div>'
WP_HTML::render(
<<<'HTML'
<textarea class="</%editor_class>" ...height tabindex="</%tabindex>"
autocomplete="</%autocomplete>" cols="40" name="</%name>" id="</%id>">%s</textarea>
HTML,
array(
'autocomplete' => self::$this_tinymce ? 'off' : null,
'editor_class' => trim( "{$set['editor_class']} wp-editor-area" ),
'height' => ! empty( $set['editor_height'] )
? array( 'style' => "height: {$set['editor_height']}px;" )
: array( 'rows' => (string) $set['textarea_rows'] ),
'id' => $editor_id,
'name' => $set['textarea_name'],
'tabindex' => $set['tabindex'] ? (string) $set['tabindex'] : null,
)
) .
'</div>'
);

// Prepare the content for the Visual or Text editor, only when TinyMCE is used (back-compat).
Expand Down Expand Up @@ -300,12 +329,16 @@ public static function editor( $content, $editor_id, $settings = array() ) {
$content = apply_filters_deprecated( 'richedit_pre', array( $content ), '4.3.0', 'format_for_editor' );
}

if ( false !== stripos( $content, 'textarea' ) ) {
$content = preg_replace( '%</textarea%i', '&lt;/textarea', $content );
$processor = new WP_HTML_Tag_Processor( $the_editor );
while ( $processor->next_tag( 'TEXTAREA' ) ) {
if ( $editor_id === $processor->get_attribute( 'id' ) ) {
$processor->set_modifiable_text( $content );
break;
}
}
$the_editor = $processor->get_updated_html();

printf( $the_editor, $content );
echo "\n</div>\n\n";
echo "{$the_editor}\n</div>\n\n";

self::editor_settings( $editor_id, $set );
}
Expand Down
75 changes: 71 additions & 4 deletions src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2038,8 +2038,8 @@ private function after_tag() {
$this->token_length = null;
$this->tag_name_starts_at = null;
$this->tag_name_length = null;
$this->text_starts_at = 0;
$this->text_length = 0;
$this->text_starts_at = null;
$this->text_length = null;
$this->is_closing_tag = null;
$this->attributes = array();
$this->comment_type = null;
Expand Down Expand Up @@ -2826,6 +2826,50 @@ public function get_modifiable_text() {
return $decoded;
}

/**
* Sets the modifiable text for the matched token, if possible.
*
* @param string $text Replace the modifiable text with this string.
* @return bool Whether the modifiable text was updated.
*/
public function set_modifiable_text( $text ) {
if ( null === $this->text_starts_at || ! is_string( $text ) ) {
return false;
}

switch ( $this->get_token_name() ) {
case '#text':
$this->lexical_updates[] = new WP_HTML_Text_Replacement(
$this->text_starts_at,
$this->text_length,
esc_html( $text )
);
break;

case 'TEXTAREA':
$this->lexical_updates[] = new WP_HTML_Text_Replacement(
$this->text_starts_at,
$this->text_length,
preg_replace( '~</textarea~i', '&lt;/textarea', $text )
);
break;

case 'TITLE':
$this->lexical_updates[] = new WP_HTML_Text_Replacement(
$this->text_starts_at,
$this->text_length,
preg_replace( '~</title~i', '&lt;/title', $text )
);
break;

default:
return false;
}

$this->get_updated_html();
return true;
}

/**
* Updates or creates a new attribute on the currently matched tag with the passed value.
*
Expand Down Expand Up @@ -2899,14 +2943,37 @@ public function set_attribute( $name, $value ) {
* > To represent a false value, the attribute has to be omitted altogether.
* - HTML5 spec, https://html.spec.whatwg.org/#boolean-attributes
*/
if ( false === $value ) {
if ( false === $value || null === $value ) {
return $this->remove_attribute( $name );
}

if ( true === $value ) {
$updated_attribute = $name;
} else {
$escaped_new_value = esc_attr( $value );
$tag_name = $this->get_tag();
$comparable_name = strtolower( $name );

/*
* Escape URL attributes.
*
* @see https://html.spec.whatwg.org/#attributes-3
*/
if (
! str_starts_with( $value, 'data:' ) && (
'cite' === $comparable_name ||
'formaction' === $comparable_name ||
'href' === $comparable_name ||
'ping' === $comparable_name ||
'src' === $comparable_name ||
( 'FORM' === $tag_name && 'action' === $comparable_name ) ||
( 'OBJECT' === $tag_name && 'data' === $comparable_name ) ||
( 'VIDEO' === $tag_name && 'poster' === $comparable_name )
)
) {
$escaped_new_value = esc_url( $value );
} else {
$escaped_new_value = esc_attr( $value );
}
$updated_attribute = "{$name}=\"{$escaped_new_value}\"";
}

Expand Down
Loading