I want to add a custom style to the wordpress tiny mce. There are tons of tutorials for just adding a simple option like "highlight" which will add a span with a "highlight" class. Like: https://torquemag.io/2016/09/add-custom-styles-wordpress-editor-manually-via-plugin/
But what I need is an option to add additional data, like if you add a link. You mark the words, hit the link button, an input for the url shows up.
What I want to achieve? A custom style "abbriation" (https://get.foundation/sites/docs/typography-base.html). The solution I'm thinking of is, the user marks the word, chooses the abbriation style, an input for the descriptions shows up. fin.
Hope you can help me out!
So I have something similar in most of my WordPress projects. I have a TinyMCE toolbar button that has a couple of fields that output a bootstrap button.
What you need to do is create your own TinyMCE "plugin" and to achieve this you need two parts:
A javascript file (your plugin)
A snippet of PHP to load your javascript (plugin) into the TinyMCE editor.
First we create the plugin:
/js/my-tinymce-plugin.js
( function() {
'use strict';
// Register our plugin with a relevant name
tinymce.PluginManager.add( 'my_custom_plugin', function( editor, url ) {
editor.addButton( 'my_custom_button', {
tooltip: 'I am the helper text',
icon: 'code', // #link https://www.tiny.cloud/docs/advanced/editor-icon-identifiers/
onclick: function() {
// Get the current selected tag (if has one)
var selectedNode = editor.selection.getNode();
// If we have a selected node, get the inner content else just get the full selection
var selectedText = selectedNode ? selectedNode.innerHTML : editor.selection.getContent();
// Open a popup
editor.windowManager.open( {
title: 'My popup title',
body: [
// Create a simple text field
{
type: 'textbox',
name: 'field_name_textbox',
label: 'Field label',
value: selectedText || 'I am a default value' // Use the selected value or set a default
},
// Create a select field
{
type: 'listbox',
name: 'field_name_listbox',
label: 'Field list',
value: '',
values: {
'value': 'Option 1',
'value-2': 'Option 2'
}
},
// Create a boolean checkbox
{
type: 'checkbox',
name: 'field_name_checkbox',
label: 'Will you tick me?',
checked: true
}
],
onsubmit: function( e ) {
// Get the value of our text field
var textboxValue = e.data.field_name_textbox;
// Get the value of our select field
var listboxValue = e.data.field_name_listbox;
// Get the value of our checkbox
var checkboxValue = e.data.field_name_checkbox;
// If the user has a tag selected
if ( selectedNode ) {
// Do something with selected node
// For example we can add a class
selectedNode.classList.add( 'im-a-custom-class' );
} else {
// Insert insert content
// For example we will create a span with the text field value
editor.insertContent( '<span>' + ( textboxValue || 'We have no value!' ) + '</span>' );
}
}
} );
}
} );
} );
} )();
Now we add and modify the below snippet to your themes functions.php file.
/functions.php
<?php
add_action( 'admin_head', function() {
global $typenow;
// Check user permissions
if ( !current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) ) {
return;
}
// Check if WYSIWYG is enabled
if ( user_can_richedit() ) {
// Push my button to the second row of TinyMCE actions
add_filter( 'mce_buttons', function( $buttons ) {
$buttons[] = 'my_custom_button'; // Relates to the value added in the `editor.addButton` function
return $buttons;
} );
// Load our custom js into the TinyMCE iframe
add_filter( 'mce_external_plugins', function( $plugin_array ) {
// Push the path to our custom js to the loaded scripts array
$plugin_array[ 'my_custom_plugin' ] = get_template_directory_uri() . '/js/my-tinymce-plugin.js';
return $plugin_array;
} );
}
} );
Make sure to update the file name and path if you it's different to this example!
WordPress uses TinyMCE 4 and the documentation for this is lacking so finding exactly what you need can be painful.
This is merely a starting point and has not been tested.
Hope this helps!
EDIT
The below code should help you with the insertion of an "abbreviations" tag and title attribute.
( function() {
'use strict';
tinymce.PluginManager.add( 'my_custom_plugin', function( editor, url ) {
editor.addButton( 'my_custom_button', {
tooltip: 'Insert an abbreviation',
icon: 'plus',
onclick: function() {
var selectedNode = editor.selection.getNode();
var selectedText = selectedNode ? selectedNode.innerHTML : editor.selection.getContent();
editor.windowManager.open( {
title: 'Insert an abbreviation',
body: [
{
type: 'textbox',
name: 'abbreviation',
label: 'The abbreviated term',
value: selectedText
},
{
type: 'textbox',
name: 'title',
label: 'The full term',
value: ''
}
],
onsubmit: function( e ) {
var abbreviation = e.data.abbreviation;
var title = e.data.title.replace( '"', '\"' );
if ( selectedNode && selectedNode.tagName === 'ABBR' ) {
selectedNode.innerText = abbreviation;
selectedNode.setAttribute( 'title', title );
} else {
editor.insertContent( '<abbr title="' + title + '">' + abbreviation + '</abbr>' );
}
}
} );
}
} );
} );
} )();
Related
I have added a third checkmark option to the "Simple Product" tab, like this:
add_filter("product_type_options", function ($product_type_options) {
$product_type_options["personalize"] = [
"id" => "_personalize",
"wrapper_class" => "show_if_simple",
"label" => "Personalize",
"description" => "personalize Products",
"default" => "no",
];
return $product_type_options;
});
add_action("save_post_product", function ($post_ID, $product, $update) {
update_post_meta(
$product->ID
, "_personalize"
, isset($_POST["_personalize"]) ? "yes" : "no"
);
}, 10, 3);
I need to hide the "Attributes" tab when this custom "Personalize" checkbox is selected. ie., if you click on the "Virtual" option checkbox, the "Shipping" tab hides. Likewise, I need the "Personalize" option checkbox to hide the "Attributes" tab upon selection.
I have tried:
add_filter('woocommerce_product_data_tabs', 'misha_product_data_tabs' );
function misha_product_data_tabs( $tabs ){
$tabs['attribute']['class'][] = 'hide_if_personalize';
return $tabs;
}
But it is not working. Can you please help?
Check screenshot: https://snipboard.io/vhqMyA.jpg
First, you have to update the meta value on the checkbox change. then you can add class hide_if_personalize if meta value is yes using this woocommerce_product_data_tabs filter hook. check below code.
add_filter("product_type_options", function ( $product_type_options ) {
$product_type_options["personalize"] = [
"id" => "_personalize",
"wrapper_class" => "show_if_simple",
"label" => "Personalize",
"description" => "personalize Products",
"default" => "no",
];
return $product_type_options;
});
add_filter('woocommerce_product_data_tabs', 'misha_product_data_tabs' );
function misha_product_data_tabs( $tabs ){
$personalize = get_post_meta( get_the_ID() , "_personalize" , true );
if( $personalize == 'yes' ){
$tabs['attribute']['class'] = 'hide_if_personalize';
}
return $tabs;
}
add_action( 'wp_ajax_hide_attribute_if_personalize', 'hide_attribute_if_personalize' );
add_action( 'wp_ajax_nopriv_hide_attribute_if_personalize', 'hide_attribute_if_personalize' );
function hide_attribute_if_personalize(){
$personalize = $_POST['personalize'];
$product_id = $_POST['product_id'];
update_post_meta( $product_id, '_personalize', $personalize );
}
function add_custom_admin_js_css(){
?>
<style type="text/css">
li.attribute_options.attribute_tab.hide_if_personalize {
display: none !important;
}
</style>
<script type="text/javascript">
(function($){
$(document).ready(function(){
$( document ).on('change','#_personalize',function(){
var personalize = 'no';
if( $(this).is(":checked") ){
$('li.attribute_options.attribute_tab').addClass('hide_if_personalize');
personalize = 'yes';
}else{
$('li.attribute_options.attribute_tab').removeClass('hide_if_personalize');
}
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: "post",
data: {personalize:personalize,product_id:$('#post_ID').val(),action:'hide_attribute_if_personalize'},
success: function( response ) {
},error: function (jqXHR, exception) {
var msg = '';
if (jqXHR.status === 0) {
msg = 'Not connect.\n Verify Network.';
} else if (jqXHR.status == 404) {
msg = 'Requested page not found. [404]';
} else if (jqXHR.status == 500) {
msg = 'Internal Server Error [500].';
} else if (exception === 'parsererror') {
msg = 'Requested JSON parse failed.';
} else if (exception === 'timeout') {
msg = 'Time out error.';
} else if (exception === 'abort') {
msg = 'Ajax request aborted.';
} else {
msg = 'Uncaught Error.\n' + jqXHR.responseText;
}
console.log(msg);
},
});
});
});
})(jQuery);
</script>
<?php }
add_action( 'admin_footer', 'add_custom_admin_js_css', 10, 1 );
Tested and works.
You can hide the "Attributes" tab via a jQuery script.
The showHideAttributeTab() script it will be activated when the page is loaded and when the "Personalize" checkbox is clicked.
It is also important to disable the <input> and <select> fields of the
add attributes form to ensure that they are not sent when saving the
product.
In fact, a user could add one or more attributes to the product, check the "Personalize" checkbox and finally save the product.
If you simply hide the elements, it is true that they will not be visible to the user, but they will still be captured by the Ajax function for saving attributes.
To prevent this it is necessary to disable any field of the Attributes tab. Then, after saving, all attributes will be removed if the "Personalize" checkbox is checked.
ADDITION
When the "Personalize" checkbox is unchecked, the "General" tab is
automatically activated.
Also, if the "Attributes" tab is active and the user selects "Personalize", it automatically activates the "General" tab to avoid white content.
// adds script in Wordpress backend to show or hide attribute tab based on custom field
add_action( 'admin_footer', 'show_hide_attribute_tab' );
function show_hide_attribute_tab() {
?>
<script type="text/javascript">
function showHideAttributeTab() {
if ( jQuery('#_personalize').is(':checked') ) {
jQuery('li.attribute_options.attribute_tab').hide();
jQuery('#product_attributes').hide();
// disable all fields of the "Attributes" tab
jQuery("#product_attributes input, #product_attributes select").each(function() {
jQuery(this).prop("disabled", true);
});
// if user enables "Attributes" tab, switch to general tab.
if ( jQuery( '.attribute_options.attribute_tab' ).hasClass( 'active' ) ) {
jQuery( '.general_options.general_tab > a' ).trigger( 'click' );
}
} else {
jQuery('li.attribute_options.attribute_tab').show();
jQuery('#product_attributes').show();
// enables all fields of the "Attributes" tab
jQuery("#product_attributes input, #product_attributes select").each(function() {
jQuery(this).prop("disabled", false);
});
// switch to general tab
jQuery("li.general_options.general_tab > a").trigger("click");
}
}
// runs the script when the page loads
showHideAttributeTab();
// runs the script when the "Personalize" checkbox is clicked
jQuery("#_personalize").click(function() {
showHideAttributeTab();
});
</script>
<?php
}
The code has been tested and works. Add it to your active theme's functions.php.
I have implemented Algolia for Wordpress and am customizing the search template in the "instantsearch.php" file.
Looking at the documentation here https://www.algolia.com/doc/api-reference/widgets/search-box/js/#widget-param-searchasyoutype setting the searchAsYouType parameter to false should prevent Algolia from detecting user input and searching while a user types. However, this parameter is not disabling the search as you type for my site.
Below is the code for my input field widget and other custom components:
/* Instantiate instantsearch.js */
var search = instantsearch({
appId: algolia.application_id,
apiKey: algolia.search_api_key,
indexName: algolia.indices.searchable_posts.name,
urlSync: {
mapping: {'q': 's'},
trackedParameters: ['query']
},
searchParameters: {
facetingAfterDistinct: true,
highlightPreTag: '__ais-highlight__',
highlightPostTag: '__/ais-highlight__'
}
});
/* Search box widget */
search.addWidget(
instantsearch.widgets.searchBox({
container: '#algolia-search-box',
placeholder: 'Search ...',
searchAsYouType: false,
wrapInput: false,
poweredBy: algolia.powered_by_enabled
})
);
/* Hits widget */
search.addWidget(
instantsearch.widgets.hits({
container: '#algolia-hits',
hitsPerPage: 10,
templates: {
empty: 'No results were found for "<strong>{{query}}</strong>".',
item: wp.template('instantsearch-hit')
},
transformData: {
item: function (hit) {
function replace_highlights_recursive (item) {
if( item instanceof Object && item.hasOwnProperty('value')) {
item.value = _.escape(item.value);
item.value = item.value.replace(/__ais-highlight__/g, '<em>').replace(/__\/ais-highlight__/g, '</em>');
} else {
for (var key in item) {
item[key] = replace_highlights_recursive(item[key]);
}
}
return item;
}
hit._highlightResult = replace_highlights_recursive(hit._highlightResult);
hit._snippetResult = replace_highlights_recursive(hit._snippetResult);
console.log(hit);
if ( hit.post_excerpt != '' ){
hit._snippetResult['content']['value'] = hit.post_excerpt;
}
if ( hit.short_title.length > 0 && hit.short_title[0] != '' ){
hit._highlightResult['post_title']['value'] = hit.short_title[0];
}
return hit;
}
}
})
);
/* Pagination widget */
search.addWidget(
instantsearch.widgets.pagination({
container: '#algolia-pagination'
})
);
/* Tags refinement widget */
search.addWidget(
instantsearch.widgets.refinementList({
container: '#facet-tags',
attributeName: 'taxonomies.aar_article_type',
operator: 'and',
limit: 15,
sortBy: ['isRefined:desc', 'count:desc', 'name:asc'],
templates: {
header: '<h2 class="widgettitle">Filter Results</h2>'
}
})
);
/* Start */
search.start();
I have added the parameter to the search box widget, but when i type, the page still auto updates without me having to hit submit. Am I missing a configuration or something?
The correct property for accomplishing this is searchOnEnterKeyPressOnly , not searchAsYouType
The proper documentation for the widget I am using can be found here https://community.algolia.com/instantsearch.js/v2/widgets/searchBox.html#struct-SearchBoxWidgetOptions-searchOnEnterKeyPressOnly
I have this code that does open Wordpress Media Uploader upon my custom button click and I have everything working from uploading image to selecting an image ... but how do I send the image/attachment to Text editor
jQuery(document).ready( function($){
var mediaUploader;
$('#_button').on('click',function(e) {
e.preventDefault();
if( mediaUploader ){
mediaUploader.open();
return;
}
mediaUploader = wp.media.frames.file_frame = wp.media( {
title : 'My Custom Library',
multiple : false,
library : { type : 'image' },
button : { text : 'Select Image' },
frame : 'post',
state : 'insert',
} );
mediaUploader.on('insert', function() {
var attachment = mediaUploader.state().get('selection').first().toJSON();
//WHAT TO DO HERE TO SEND THIS TO TEXT EDITOR??????
});
mediaUploader.open();
}); });
Found the answer myself from
https://core.trac.wordpress.org/browser/tags/4.9.8/src/wp-includes/js/media-editor.js#L852
mediaUploader.on('insert', function() {
var embed = mediaUploader.state().get( 'selection' ).first().toJSON();
_.defaults( embed, {
title: embed.url,
linkUrl: '',
align: 'none',
link: 'none'
});
if ( 'none' === embed.link ) {
embed.linkUrl = '';
} else if ( 'file' === embed.link ) {
embed.linkUrl = embed.url;
}
wp.media.editor.insert( wp.media.string.image( embed ) );
});
I have the following code that I need to be able to edit the template of the ckeditor widget. I have learned that templates are immutable. What I want to achieve is to be able to insert a variable in the template. Is it achievable?
( function($, Drupal, drupalSettings, CKEDITOR) {
CKEDITOR.plugins.add('tabslinks',{
requires: 'widget',
lang: 'en',
icons: 'tabslinks',
hidpi: true, // %REMOVE_LINE_CORE%
init: function(editor) {
editor.ui.addButton('tabslinks', {
label: 'Create tabs links',
command: 'tabslinks',
icon: this.path + 'icons/tabslinks.png'
});
editor.addContentsCss(this.path + 'assets/contents.css');
editor.widgets.add('tabslinks',{
allowedContent: 'a(!tabslinks), a[!href];',
requiredContent: 'a',
editables: {
title: {
selector: '.tabslinks'
}
},
template: '<a class="tabslinks" href="" >' +
'Link should be a variable such as the result of var tabtitle ' +
'</a>',
button: 'Create tab title and link',
init: function () {
var tabtitle = this.element.getAttribute('data-cke-saved-href');
if(tabtitle){
this.setData('tabtitle',tabtitle);
}
},
upcast: function(element) {
return element.name == 'a' && element.hasClass('.tabslinks');
},
dialog: 'tabslinks',
data: function() {
/* Note I can edit the attributes in the following without a problem. The problem is that I cannot manipulate the dom, I have used methhods such as editor CKEDITOR.dom.element.createFromHtml(html) but that also breaks the widget, I have also tried to use jquery with no luck */
if(this.data.tabtitle){
this.element.setAttribute('data-cke-saved-href','#' + this.data.tabtitle);
this.element.setAttribute('data-toggle','tab');
}
}
} );
CKEDITOR.dialog.add( 'tabslinks', this.path + 'dialogs/tabslinks.js' );
}
} );
} )(jQuery, Drupal, drupalSettings, CKEDITOR);
I have tried to use many methods trying to manipulating the dom but this breaks the widget. Any suggestions?
I want to remove the Add to Cart button and display View Cart in its place,after product is added in woo-commerce.
How do i do so?
Please give some hint.
what i have been implementing for my projects
add following code in function.php to deregister the js which add view cart button along with add to cart button
wp_deregister_script('wc-add-to-cart');
wp_register_script('wc-add-to-cart', get_stylesheet_directory_uri(). '/js/add-to-cart-multi.js' , array( 'jquery' ), WC_VERSION, TRUE);
wp_enqueue_script('wc-add-to-cart');
Now you need to tweak that js that we have deregistered and now we need to make a new js file named add-to-cart-multi.js
Following code goes in this newly created file
jQuery( function( $ ) {
// wc_add_to_cart_params is required to continue, ensure the object exists
if ( typeof wc_add_to_cart_params === 'undefined' )
return false;
// Ajax add to cart
$( document ).on( 'click', '.add_to_cart_button', function(e) {
// AJAX add to cart request
var $thisbutton = $( this );
if ( $thisbutton.is( '.product_type_simple' ) ) {
if ( ! $thisbutton.attr( 'data-product_id' ) )
return true;
$thisbutton.removeClass( 'added' );
$thisbutton.addClass( 'loading' );
var data = {
action: 'woocommerce_add_to_cart',
};
$.each( $thisbutton.data(), function( key, value ) {
data[key] = value;
});
// Trigger event
$( 'body' ).trigger( 'adding_to_cart', [ $thisbutton, data ] );
// Ajax action
$.post( wc_add_to_cart_params.ajax_url, data, function( response ) {
if ( ! response )
return;
var this_page = window.location.toString();
this_page = this_page.replace( 'add-to-cart', 'added-to-cart' );
if ( response.error && response.product_url ) {
window.location = response.product_url;
return;
}
// Redirect to cart option
if ( wc_add_to_cart_params.cart_redirect_after_add === 'yes' ) {
window.location = wc_add_to_cart_params.cart_url;
return;
} else {
$thisbutton.removeClass( 'loading' );
fragments = response.fragments;
cart_hash = response.cart_hash;
// Block fragments class
if ( fragments ) {
$.each( fragments, function( key, value ) {
$( key ).addClass( 'updating' );
});
}
// Block widgets and fragments
$( '.shop_table.cart, .updating, .cart_totals' ).fadeTo( '400', '0.6' ).block({
message: null,
overlayCSS: {
opacity: 0.6
}
});
// Changes button classes
$thisbutton.addClass( 'added' );
$thisbutton.addClass( 'hide' );
// View cart text
if ( ! wc_add_to_cart_params.is_cart && $thisbutton.parent().find( '.added_to_cart' ).size() === 0 ) {
$thisbutton.after( ' <a href="' + wc_add_to_cart_params.cart_url + '" class="add-cart button added_to_cart wc-forward" title="' +
wc_add_to_cart_params.i18n_view_cart + '"><i class="fa fa-shopping-cart"></i></a>' );
}
// Replace fragments
if ( fragments ) {
$.each( fragments, function( key, value ) {
$( key ).replaceWith( value );
});
}
// Unblock
$( '.widget_shopping_cart, .updating' ).stop( true ).css( 'opacity', '1' ).unblock();
// Cart page elements
$( '.shop_table.cart' ).load( this_page + ' .shop_table.cart:eq(0) > *', function() {
$( '.shop_table.cart' ).stop( true ).css( 'opacity', '1' ).unblock();
$( 'body' ).trigger( 'cart_page_refreshed' );
});
$( '.cart_totals' ).load( this_page + ' .cart_totals:eq(0) > *', function() {
$( '.cart_totals' ).stop( true ).css( 'opacity', '1' ).unblock();
});
// Trigger event so themes can refresh other areas
$( 'body' ).trigger( 'added_to_cart', [ fragments, cart_hash ] );
}
});
return false;
}
return true;
});
});
On line number 77 i have added class="hide" using code
$thisbutton.addClass( 'hide' );
So hide basically hides the add to cart button
And did some more modifications i think to the code , but i dont remember it now what was it.
I hope this helps somebody
Works easily using just CSS on your theme/child style:
.add_to_cart_button.added {display:none !important;}
i stumbled across this whilst looking to do eaxctly the same thing, and although i couldn't get the js to work i did notice when looking for the added .hide class that woo adds an .added class to the 'add to cart' button once it's been clicked anyway, so adding the following into my theme's css file did the job nicely.
a.button.product_type_simple.add_to_cart_button.ajax_add_to_cart.added {
display: none;
}
i then simply styled the 'view cart' button to appear in its place, using this to target it:
a.added_to_cart {
//do your thing
}
hope that's helpful to someone.