// ✅ Enable a fully functional Rich Text Editor field in Gravity Forms
// Works on frontend + shows formatted output (not HTML tags)
//Enqueue editor scripts (TinyMCE + Media)
add_action('gform_enqueue_scripts', function() {
wp_enqueue_editor();
wp_enqueue_media();
});
// 1. Add “Rich Text Editor” button to Form Builder
add_filter('gform_add_field_buttons', function($field_groups){
$field_groups[] = array(
'name' => 'custom_fields',
'label' => __('Custom Fields', 'gravityforms'),
'fields' => array(
array(
'class' => 'button',
'value' => __('Rich Text Editor', 'gravityforms'),
'data-type' => 'editor',
),
),
);
return $field_groups;
});
// 2. Register field type name
add_filter('gform_field_type_title', function($type){
return $type === 'editor' ? __('Rich Text Editor', 'gravityforms') : $type;
});
// 3. Add standard settings (optional)
add_action('gform_field_standard_settings', function($position, $form_id){
if($position == 25){ ?>
type !== 'editor') return $input;
ob_start();
wp_editor(
$value,
'input_' . $form_id . '_' . $field->id,
array(
'textarea_name' => 'input_' . $field->id,
'media_buttons' => true,
'quicktags' => true,
'tinymce' => array(
'height' => 300,
'toolbar1' => 'formatselect bold italic underline | bullist numlist blockquote | alignleft aligncenter alignright link unlink | removeformat',
),
)
);
$editor_html = ob_get_clean();
return sprintf(
'%s
',
$editor_html
);
}, 10, 5);
// Safe rendering — decode and render formatted HTML (not tags)
if ( ! function_exists( 'gf_rich_allowed_tags' ) ) {
function gf_rich_allowed_tags() {
return array(
'p' => array(),
'br' => array(),
'strong' => array(),
'b' => array(),
'em' => array(),
'i' => array(),
'u' => array(),
'span' => array( 'style' => true, 'class' => true ),
'div' => array( 'style' => true, 'class' => true ),
'a' => array( 'href' => true, 'title' => true, 'target' => true, 'rel' => true ),
'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(),
'ul' => array(), 'ol' => array(), 'li' => array(),
'blockquote' => array(),
'img' => array( 'src' => true, 'alt' => true, 'width' => true, 'height' => true ),
'strong' => array(), 'em' => array()
);
}
}
/* ---------- Allow safe inline CSS properties ---------- */
add_filter( 'safe_style_css', function( $styles ) {
$custom = array(
'text-decoration',
'color',
'background-color',
'font-size',
'font-weight',
'font-style',
'text-align',
'line-height',
'font-family',
);
return array_unique( array_merge( $styles, $custom ) );
});
/* ---------- Helper to decode + sanitize editor HTML ---------- */
if ( ! function_exists( 'gf_render_editor_html_safe' ) ) {
function gf_render_editor_html_safe( $raw ) {
if ( $raw === null || $raw === '' ) {
return '';
}
// 1) Decode HTML entities (handles encoded "<" as <)
$decoded = html_entity_decode( (string) $raw, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
// 2) Fix possible double-encoding like <
if ( strpos( $decoded, '<' ) !== false || strpos( $decoded, '>' ) !== false ) {
$decoded = str_ireplace( array( '<', '>' ), array( '<', '>' ), $decoded );
$decoded = html_entity_decode( $decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
}
// 3) Sanitize with allowed tags (keeps , , etc.)
return wp_kses( $decoded, gf_rich_allowed_tags() );
}
}
/* ---------- Merge-tag replacement: decode + render editor fields (priority 5) ---------- */
add_filter( 'gform_replace_merge_tags', function( $text, $form, $entry, $url_encode, $esc_html, $nl2br ) {
if ( empty( $form ) || empty( $entry ) ) {
return $text;
}
// decode the container text early (handles already-encoded merge outputs)
$text = html_entity_decode( (string) $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
foreach ( $form['fields'] as $field ) {
if ( isset( $field->type ) && $field->type === 'editor' ) {
$id = $field->id;
$raw = rgar( $entry, $id );
// render the raw field value safely (allows links, styles)
$rendered = gf_render_editor_html_safe( $raw );
// Replace merge tags: {ID} and {Label:ID} and {Label:ID:modifier}
$text = str_replace( '{' . $id . '}', $rendered, $text );
$pattern = '/\{[^}]*:' . preg_quote( $id, '/' ) . '(?::[^}]*)?\}/';
$text = preg_replace( $pattern, $rendered, $text );
}
}
// Final pass: ensure we haven't left encoded common tags; decode conservative patterns
if ( strpos( $text, '<' ) !== false || strpos( $text, '<' ) !== false ) {
$search = array(
'<br />','<br>','<br/>',
'<p>','</p>',
'<h1>','</h1>','<h2>','</h2>','<h3>','</h3>',
'<h4>','</h4>','<h5>','</h5>','<h6>','</h6>',
'<strong>','</strong>','<b>','</b>',
'<em>','</em>','<i>','</i>','<u>','</u>',
'<span>','</span>','<div>','</div>',
'<a href="','</a>',
'<','>'
);
$replace = array(
'
','
','
',
'','
',
'','
','','
','','
',
'','
','','
','','
',
'','','','',
'','','','','','',
'','','','
',
' '',
'field' => '',
), $atts, 'gf_editor' );
$entry_id = intval( $atts['entry'] );
$field_id = intval( $atts['field'] );
if ( $entry_id <= 0 || $field_id <= 0 ) return '';
$entry = GFAPI::get_entry( $entry_id );
if ( is_wp_error( $entry ) ) return '';
$raw = rgar( $entry, $field_id );
return gf_render_editor_html_safe( $raw );
} );
}