Skip to content

Instantly share code, notes, and snippets.

@rahulsprajapati
Last active April 30, 2021 21:32
Show Gist options
  • Save rahulsprajapati/066f61e97f934ff82dc6f77bc89bf522 to your computer and use it in GitHub Desktop.
Save rahulsprajapati/066f61e97f934ff82dc6f77bc89bf522 to your computer and use it in GitHub Desktop.
Custom Shortcode UI Functionality: TinyMce And Repeater
<?php
/**
* Accordion ShortCode.
*
* @param array $accordion_attrs Accordion ShortCode attribute.
*
* @return string
*/
function accordion_register_shortcode( $accordion_attrs ) {
// Attributes defined for repeater section.
$accordion_default_attrs = array(
'question' => '',
'answer' => '',
);
$repeater_field_count = get_repeater_shortcode_attr_count( $accordion_attrs, $accordion_default_attrs );
if ( $repeater_field_count <= 0 ) {
return '';
}
$content = '<div class="layout__block">';
for ( $i = 0; $i < $repeater_field_count; $i ++ ) {
if ( empty( $accordion_attrs[ 'question-' . $i ] ) || empty( $accordion_attrs[ 'answer-' . $i ] ) ) {
continue;
}
$content .= '<div class="accordion">';
$content .= '<div class="js-accordionTrigger">';
$content .= sprintf( '<div class="accordion__title">%s</div>', esc_html( $accordion_attrs[ 'question-' . $i ] ) );
$content .= '</div>';
$content .= sprintf( '<div>%s</div>', wp_kses_post( rawurldecode( $accordion_attrs[ 'answer-' . $i ] ) ) );
$content .= '</div>';
}
$content .= '</div>';
return $content;
}
add_shortcode( 'accordion', 'accordion_register_shortcode' );
/**
* Register Accordion ShortCode UI.
*/
function accordion_register_shortcode_ui() {
// Register ShortCode ui for Accordion.
shortcode_ui_register_for_shortcode( 'accordion',
array(
'label' => esc_html__( 'Add Accordion', 'festival' ),
'listItemImage' => 'dashicons-list-view',
'post_type' => array( 'page', 'post' ),
'attrs' => array(
array(
'label' => esc_html__( 'Question', 'festival' ),
'attr' => 'question-0',
'type' => 'text',
),
array(
'label' => esc_html__( 'Answer', 'festival' ),
'attr' => 'answer-0',
'type' => 'textarea',
'encode' => true,
'meta' => array(
'data-wysiwyg' => 'true',
),
),
),
)
);
}
add_action( 'register_shortcode_ui', 'accordion_register_shortcode_ui' );
/**
* Style for Shortcode UI content editor with tinymce.
*/
.shortcode-ui-model-open .field-block .mce-tinymce {
border: 1px solid #ddd;
}
.shortcode-ui-model-open .mce-panel.mce-menu, .shortcode-ui-model-open .mce-tooltip, .shortcode-ui-model-open .mce-inline-toolbar-grp, .shortcode-ui-model-open .ui-autocomplete.wplink-autocomplete, .shortcode-ui-model-open #wp-link-wrap {
z-index: 160001 !important;
}
.shortcode-ui-repeater-section {
border: 1px solid;
padding: 10px;
margin-bottom: 10px;
}
.repeater-new-section-container {
padding: 15px 0;
}
.repeater-new-section-button {
padding: 10px;
cursor: pointer;
}
/**
* Script for shortcode UI customization.
* Add tinyMCE and repeater Field.
*/
(function( $, wp ) {
'use strict';
// Shorcodes for which to enable tinyMCE on inner content field.
var supportedShortcodeTagsForWysiwyg = [ 'accordion' ];
var supportedShortcodeTagsForRepeater = [ 'accordion' ];
window.CustomShortcodeUI = {
// Function to load TinyMCE Editor to selector.
loadEditor: function( shortcode, selectorId ) {
var selector = '.shortcode-ui-edit-' + shortcode.get('shortcode_tag') + ' #' + selectorId;
var _that = this;
if( 'undefined' === typeof selectorId ) {
selector = '.shortcode-ui-edit-' + shortcode.get('shortcode_tag') + ' textarea[data-wysiwyg]';
}
if ( $( selector ).length > 1 ) {
$( selector ).each( function() {
selectorId = $( this ).attr( 'id' );
_that.initTinyMce( selector, selectorId );
} );
} else {
selectorId = $( selector ).attr( 'id' );
_that.initTinyMce( selector, selectorId );
}
return true;
},
initTinyMce: function( selector, selectorId ) {
var editor = tinymce.get(selectorId);
// Check if editor is already initialized, destroy before re-init.
if ( editor ) {
editor.destroy();
}
// Initialize tinymce editor to selector textarea.
tinyMCE.init({
selector: selector,
id: selectorId,
content_css: tinyMCEPreInit.mceInit.content.content_css,
menubar: false,
relative_urls: true,
height: 200,
toolbar: 'formatselect,bold,italic,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,wp_more',
plugins: [ 'lists', 'paste', 'wordpress', 'wplink' ],
init_instance_callback: function( editor ) {
editor.on( 'change', function() {
var $target = $( editor.targetElm );
$target.val( editor.getContent( { format: 'raw' } ) );
$target.val( tinymce.html.Entities.decode( $target.val().trim() ) );
$target.trigger( 'input' );
});
}
});
},
// Function to unload TinyMCE Editor to selector.
unloadEditor: function( shortcode ) {
var selectorId,
selector = '.shortcode-ui-edit-' + shortcode.get('shortcode_tag') + ' textarea[data-wysiwyg]';
if ( $( selector ).length > 1 ) {
$( selector ).each( function() {
selectorId = $( this ).attr( 'id' );
var editor = tinymce.get(selectorId);
if ( editor ) {
editor.destroy();
}
} );
} else {
selectorId = $( selector ).attr('id');
var editor = tinymce.get(selectorId);
if ( editor ) {
editor.destroy();
}
}
},
addRepeaterSupport: function ( shortcode ) {
var shortcodeTag = shortcode.get('shortcode_tag');
var selector = '.shortcode-ui-edit-' + shortcodeTag;
var shortcodeContainer = $( selector ).parent( '.edit-shortcode-form' );
if ( supportedShortcodeTagsForWysiwyg.indexOf( shortcodeTag ) >= 0 ) {
this.unloadEditor( shortcode );
}
$( shortcodeContainer ).addClass( 'shortcode-ui-repeater-container' );
$( selector ).addClass('shortcode-ui-repeater-section');
var addNewFieldButton = $( '<button/>', {
class: 'repeater-new-section-button',
text: 'Add New Section',
type: 'button'
} );
var addNewFieldContainer = $( '<div/>', {
class: 'repeater-new-section-container',
} );
var repeaterFieldsContainer = $( '<div/>', {
class: 'repeater-fields-container'
} );
$( selector ).appendTo( repeaterFieldsContainer );
$( shortcodeContainer ).append( repeaterFieldsContainer );
$( addNewFieldContainer ).append( addNewFieldButton );
$( shortcodeContainer ).append( addNewFieldContainer );
if ( supportedShortcodeTagsForWysiwyg.indexOf( shortcodeTag ) >= 0 ) {
this.loadEditor( shortcode );
}
this.addNewSection( shortcode, repeaterFieldsContainer );
},
addNewSection: function( shortcode, repeaterFieldsContainer ) {
var EditShortcodeForm = sui.views.EditShortcodeForm;
var shortcodeAttrs = $.extend( true, {}, shortcode.get( 'attrs' ) );
var shortcodeAttrsCount = shortcodeAttrs.length;
var shortcodeView = new EditShortcodeForm({ model: shortcode });
var updatedCid = '';
var increment = 1;
var _this = this;
var shortcodeRepeaterAttrs = shortcode.get('attributes_backup');
var shortcodeRepeaterAttrsLength = Object.keys( shortcodeRepeaterAttrs ).length;
var editExistingRepeaterShortcode = ( shortcodeRepeaterAttrsLength > 0 );
var wysiwygEditors = [];
shortcode.set( 'attributes_backup', {} );
if( editExistingRepeaterShortcode ) {
var extraAttrsIndex = {};
$.each( shortcodeRepeaterAttrs, function ( key ) {
var splitKeys = key.split('-');
var index = parseInt( splitKeys.pop() );
extraAttrsIndex[ index ] = index;
});
var existingRepeaterSectionCount = Object.keys( extraAttrsIndex ).length;
for ( var i = 1; i <= existingRepeaterSectionCount; i++ ) {
var newSectionContainer = $(
'<div/>',
{ class: 'edit-shortcode-form-fields shortcode-ui-repeater-section shortcode-ui-edit-' + shortcode.get('shortcode_tag') }
);
shortcodeAttrs.each( function ( attr, index ) {
var newAttrs = $.extend( true, {}, attr );
var type = newAttrs.get('type');
var newAttr = newAttrs.get('attr');
var metaData = newAttrs.get( 'meta' );
var newAttrValue;
if ( ! shortcodeUIFieldData[type] ) {
return;
}
newAttr = newAttr.substring( 0, newAttr.length - 2 );
newAttr = newAttr + '-' + increment;
newAttrs.set('attr', newAttr);
if ( 'undefined' !== typeof shortcodeRepeaterAttrs[newAttr] ) {
newAttrValue = shortcodeRepeaterAttrs[newAttr];
if (newAttrs && newAttrs.get('encode')) {
newAttrValue = decodeURIComponent(newAttrValue);
newAttrValue = newAttrValue.replace("&#37;", "%");
}
newAttrs.set('value', newAttrValue);
}
updatedCid = 'c' + ( parseInt( newAttrs.cid.substring(1) ) + ( shortcodeAttrsCount * increment ) );
newAttrs.set('id', 'shortcode-ui-' + newAttrs.get('attr') + '-' + updatedCid);
newAttrs.cid = updatedCid;
shortcode.attributes.attrs.models.push( newAttrs );
var viewObjName = shortcodeUIFieldData[type].view;
var tmplName = shortcodeUIFieldData[type].template;
var view = new sui.views[viewObjName]({model: newAttrs});
view.template = wp.media.template(tmplName);
view.shortcode = shortcodeView.model;
shortcodeView.views.add( '.edit-shortcode-form-fields', view );
view.render();
if( 'undefined' !== typeof metaData['data-wysiwyg'] ) {
wysiwygEditors.push( newAttrs.get('id') );
}
$(newSectionContainer).append( view.el );
} );
$( repeaterFieldsContainer ).append( newSectionContainer );
shortcode._previousAttributes = shortcode.attributes;
shortcode.attributes.attrs.length = shortcode.attributes.attrs.length + shortcodeAttrsCount;
increment++;
}
}
if( wysiwygEditors.length > 0 ) {
$.each( wysiwygEditors, function ( index, value ) {
_this.loadEditor( shortcode, value );
});
}
wysiwygEditors = [];
$( '.repeater-new-section-button' ).on( 'click', function( e ) {
// TODO: Add in one function.
var newSectionContainer = $(
'<div/>',
{ class: 'edit-shortcode-form-fields shortcode-ui-repeater-section shortcode-ui-edit-' + shortcode.get('shortcode_tag') }
);
shortcodeAttrs.each( function ( attr, index ) {
var newAttrs = $.extend( true, {}, attr );
var type = newAttrs.get('type');
var newAttr = newAttrs.get('attr');
var metaData = newAttrs.get( 'meta' );
newAttr = newAttr.substring( 0, newAttr.length - 2 );
newAttrs.set('attr', newAttr + '-' + increment);
newAttrs.set('value', '');
updatedCid = 'c' + ( parseInt( newAttrs.cid.substring(1) ) + shortcodeAttrsCount );
newAttrs.set('id', 'shortcode-ui-' + newAttrs.get('attr') + '-' + updatedCid);
newAttrs.cid = updatedCid;
if ( ! shortcodeUIFieldData[type] ) {
return;
}
var viewObjName = shortcodeUIFieldData[type].view;
var tmplName = shortcodeUIFieldData[type].template;
var view = new sui.views[viewObjName]({model: newAttrs});
view.template = wp.media.template(tmplName);
view.shortcode = shortcodeView.model;
shortcodeView.views.add( '.edit-shortcode-form-fields', view );
view.render();
if( 'undefined' !== typeof metaData['data-wysiwyg'] ) {
wysiwygEditors.push( newAttrs.get('id') );
}
$(newSectionContainer).append( view.el );
shortcode.attributes.attrs.models.push( newAttrs );
});
$( repeaterFieldsContainer ).append( newSectionContainer );
if( wysiwygEditors.length > 0 ) {
$.each( wysiwygEditors, function ( index, value ) {
_this.loadEditor( shortcode, value );
});
}
increment++;
});
}
};
// Shortcode ui plugin render_edit shortcode action.
wp.shortcake.hooks.addAction( 'shortcode-ui.render_edit', function( shortcode ) {
// Need to handle css based on this class. Default model-open class is not working properly when we use attachement in shortcode-ui.
$( 'body' ).addClass('shortcode-ui-model-open');
if ( supportedShortcodeTagsForRepeater.indexOf( shortcode.get( 'shortcode_tag' ) ) >= 0 ) {
CustomShortcodeUI.addRepeaterSupport( shortcode );
} else if ( supportedShortcodeTagsForWysiwyg.indexOf( shortcode.get( 'shortcode_tag' ) ) >= 0 ) {
CustomShortcodeUI.loadEditor( shortcode );
}
} );
// Shortcode ui plugin render_new shortcode action.
wp.shortcake.hooks.addAction( 'shortcode-ui.render_new', function( shortcode ) {
// Need to handle css based on this class. Default model-open class is not working properly when we use attachement in shortcode-ui.
$( 'body' ).addClass('shortcode-ui-model-open');
if ( supportedShortcodeTagsForRepeater.indexOf( shortcode.get( 'shortcode_tag' ) ) >= 0 ) {
CustomShortcodeUI.addRepeaterSupport( shortcode );
} else if ( supportedShortcodeTagsForWysiwyg.indexOf( shortcode.get( 'shortcode_tag' ) ) >= 0 ) {
CustomShortcodeUI.loadEditor( shortcode );
}
} );
// Shortcode ui plugin render_destroy shortcode action.
wp.shortcake.hooks.addAction( 'shortcode-ui.render_destroy', function( shortcode ) {
// Need to handle css based on this class. Default model-open class is not working properly when we use attachement in shortcode-ui.
$( 'body' ).removeClass('shortcode-ui-model-open');
if ( supportedShortcodeTagsForWysiwyg.indexOf( shortcode.get( 'shortcode_tag' ) ) >= 0 ) {
CustomShortcodeUI.unloadEditor( shortcode );
}
} );
})( jQuery, window.wp );
<?php
/**
* Admin Enqueue Scripts.
*/
function custom_admin_enqueue_scripts() {
// Admin Custom style.
wp_enqueue_style( 'custom-admin-style', get_template_directory_uri() . 'css/admin-style.css' );
}
add_action( 'admin_enqueue_scripts', 'custom_admin_enqueue_scripts' );
/**
* Shortcode UI customization.
*/
function custom_shortcode_ui_scripts() {
// Shortcode ui customization script.
wp_enqueue_script( 'custom-shortcode-ui', get_template_directory_uri() . 'js/custom-shortcode-ui.js', array( 'shortcode-ui' ), '1.0.0', true );
}
add_action( 'enqueue_shortcode_ui', 'custom_shortcode_ui_scripts' );
/**
* Get repeater section count for shortcode attributes.
*
* @param array $attrs Shortcode Attributes.
* @param array $defined_attrs Shortcode Defined Attributes.
*
* @return int
*/
function get_repeater_shortcode_attr_count( $attrs, $defined_attrs ) {
$repeater_attrs_indexes = [];
foreach ( $attrs as $key => $value ) {
$index = substr( $key, 0, strrpos( $key, '-' ) );
if ( array_key_exists( $index, $defined_attrs ) ) {
$index = substr( $key, strrpos( $key, '-' ) + 1 );
$repeater_attrs_indexes[ $index ] = true;
}
}
return count( $repeater_attrs_indexes );
}
@rahulsprajapati
Copy link
Author

For repeater shortcode ui we need to expose EditShortcodeForm View for reuse markup.
ref: wp-shortcake/shortcake#773

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment