/** * Callback for RegEx replacement of shortcodes. * * Calls and wraps all shortcodes in the content. * * @since 1.0.0 * @access private * * @global array $render_shortcode_data Extra data supplied from JS. * @global array $shortcode_tags WP registered shortcodes. * * @param array $matches Matches supplied from preg_replace_callback(), * @return string The substituted output. */ public static function _replace_shortcodes($matches) { global $render_shortcode_data, $shortcode_tags; // "Extract" some of the found matches $entire_code = $matches[0]; $code = $matches[2]; $atts = $matches[3]; $_content = $matches[5]; // Get our shortcode data $data = $render_shortcode_data[$code]; // Get our atts $atts = shortcode_parse_atts($atts); // Nested shortcode children if (isset($data['nested']['parent'])) { // Don't allow this shortcode to be edited $data['hideActions'] = true; // Set default dummy content if (!isset($data['dummyContent'])) { $data['dummyContent'] = '(Enter section content)'; } } // Search again for any nested shortcodes (loops infinitely) if (!empty($_content)) { $pattern = get_shortcode_regex(); $content = preg_replace_callback("/{$pattern}/s", array(__CLASS__, '_replace_shortcodes'), $_content); } // If the shortcode explicitely said to leave alone, completely pass over if (isset($data['ignore']) && ($data['ignore'] == true || $data['ignore'] == 'true')) { return $entire_code; } // If this is a wrapping code, but no content is provided, use dummy content if (empty($content) && isset($data['wrapping']) && $data['wrapping'] === 'true') { if (isset($data['dummyContent'])) { $content = $data['dummyContent']; } else { $content = __('No content selected.', 'Render'); } } // Properly wrap the content if (!empty($content)) { // Wrap the content in a special element, but first decide if it needs to be div or span $tag = preg_match(render_block_regex(), $content) ? 'div' : 'span'; // Override tag $tag = isset($data['displayBlock']) ? 'div' : $tag; $editable = isset($data['contentNonEditable']) ? '' : 'render-tinymce-editable'; $content = "<{$tag} class='render-tinymce-shortcode-content {$editable}'>{$content}</{$tag}>"; } // Replace the content with the new content if (!empty($content)) { $entire_code = str_replace("]{$_content}[", "]{$content}[", $entire_code); } // Get the atts prepared for JSON if (!empty($atts)) { $atts = json_encode($atts); } // Check for tinymce callback if (is_callable($shortcode_tags[$code] . '_tinymce')) { $shortcode_tags[$code] = $shortcode_tags[$code] . '_tinymce'; } // Get the shortcode output (if rendering set) if (!isset($data)) { $shortcode_output = $entire_code; } elseif (isset($data['useText'])) { $shortcode_output = $data['useText']; } else { $shortcode_output = do_shortcode($entire_code); // Un-escape the output $shortcode_output = render_sc_attr_unescape($shortcode_output); // Make sure images are non-editable (unless told otherwise) if (!isset($data['wrapping']) || $data['wrapping'] == 'false' || $data['wrapping'] == false) { $shortcode_output = preg_replace('/<img/', '<img data-mce-placeholder="1" style="outline: none !important;"', $shortcode_output); } } // If the output contains any block tags, make sure the wrapper tag is a div $tag = preg_match(render_block_regex(), $shortcode_output) ? 'div' : 'span'; // Override tag $tag = isset($data['displayBlock']) ? 'div' : $tag; $classes = array(); // The code $classes[] = $code; // Whether or not to style the code $classes[] = !isset($data['noStyle']) ? 'styled' : ''; // If the code should be forced as displayBlock $classes[] = isset($data['displayBlock']) ? 'block' : ''; // If the shortcode is a nested child $classes[] = isset($data['nested']['parent']) ? 'nested-child' : ''; // Hidden tooltip $classes[] = isset($data['hideActions']) ? 'hide-actions' : ''; /** * Allows external filtering of the wrapper classes. * * @since 1.1-beta-2 */ $classes = apply_filters("render_tinymce_shortcode_wrapper_classes_{$code}", $classes); // Parse the atts if (!empty($atts)) { $atts = htmlentities(preg_replace('/<br.*?\\/>/', '::br::', $atts)); } $output = ''; // Start the wrapper $output .= "<{$tag} class='render-tinymce-shortcode-wrapper render-tinymce-noneditable " . implode(' ', $classes) . "' data-code='{$code}' data-atts='{$atts}'>"; $output .= !empty($shortcode_output) ? $shortcode_output : '<span class="render-shortcode-no-output">(no output)</span>'; // Close the wrapper // Delete notification $output .= '<span class=\'render-tinymce-shortcode-wrapper-delete render-tinymce-tooltip\'>' . __('Press again to delete', 'Render') . "</span>"; // Action button if (!isset($data['hideActions'])) { $output .= '<span class="render-tinymce-shortcode-wrapper-actions render-tinymce-tooltip">'; $output .= '<span class="render-tinymce-tooltip-spacer"></span>'; $output .= '<span class="render-tinymce-shortcode-wrapper-edit dashicons dashicons-edit">edit</span>'; $output .= '<span class="render-tinymce-shortcode-wrapper-remove dashicons dashicons-no">remove</span>'; $output .= '</span>'; } $output .= "</{$tag}>"; return $output; }
/** * Wraps content appropriately based on its existence, for use within the TinyMCE. * * @since 1.0.0 * @param string $content The content output. * @param string $visibility Whether to hide or show content. Accepts 'hidden' or 'visible'. * @return string The appropriately wrapped content. */ function render_tinymce_visibility_wrap($content = '', $visibility = 'visible') { $tag = preg_match(render_block_regex(), $content) ? 'div' : 'span'; return "<{$tag} class='render-content-{$visibility}'>" . do_shortcode($content) . "<span class='render-visibility-icon dashicons dashicons-" . ($visibility == 'visible' ? 'visibility' : 'no') . "'></span></{$tag}>"; }