Disable Algolia for WP SearchAsYouType - wordpress

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

Related

Custom field not saved

I try to add a custom user field to the user by using WPGraphQL. Therefore I tried to recreate the example in the official WPGraphQL documentation https://docs.wpgraphql.com/extending/fields/#register-fields-to-the-schema :
add_action('graphql_init', function () {
$hobbies = [
'type' => ['list_of' => 'String'],
'description' => __('Custom field for user mutations', 'your-textdomain'),
'resolve' => function ($user) {
$hobbies = get_user_meta($user->userId, 'hobbies', true);
return !empty($hobbies) ? $hobbies : [];
},
];
register_graphql_field('User', 'hobbies', $hobbies);
register_graphql_field('CreateUserInput', 'hobbies', $hobbies);
register_graphql_field('UpdateUserInput', 'hobbies', $hobbies);
});
I already changed the type from \WPGraphQL\Types::list_of( \WPGraphQL\Types::string() ) to ['list_of' => 'String'].
If I now execute the updateUser mutation my hobbies don't get updated. What am I dowing wrong?
Mutation:
mutation MyMutation {
__typename
updateUser(input: {clientMutationId: "tempId", id: "dXNlcjox", hobbies: ["football", "gaming"]}) {
clientMutationId
user {
hobbies
}
}
}
Output:
{
"data": {
"__typename": "RootMutation",
"updateUser": {
"clientMutationId": "tempId",
"user": {
"hobbies": []
}
}
}
}
Thanks to xadm, the only thing I forgot was to really mutate the field. I was a bit confused by the documentation, my fault. (I really am new to WPGraphQL btw)
Here's what has to be added:
add_action('graphql_user_object_mutation_update_additional_data', 'graphql_register_user_mutation', 10, 5);
function graphql_register_user_mutation($user_id, $input, $mutation_name, $context, $info)
{
if (isset($input['hobbies'])) {
// Consider other sanitization if necessary and validation such as which
// user role/capability should be able to insert this value, etc.
update_user_meta($user_id, 'hobbies', $input['hobbies']);
}
}

WordPress TinyMCE custom styles with input option

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>' );
}
}
} );
}
} );
} );
} )();

Wordpress Gutenberg Anchor Support for Dynamic Block

I want to have anchor support for my dynamic wordpress block. I did
//in registerBlockType
supports: {
anchor: true,
},
This adds the HTML Anchor control under the sidebar panel.
My block is a dynamic block that has
save: ( props ) => {
return <InnerBlocks.Content />;
}
I tried everything to get the anchor attribute to to frontend. According to this github issue I should add
anchor: {
type: 'string',
source: 'attribute',
attribute: 'id',
selector: '*',
},
to the blocks attributes. This will make the anchor available in the save function via props.anchor, however it never appears in my render_callback $attributes.
This is basically a port of the github issue to SO. Hope anyone can help here.
if anyone is still interested this worked for me:
so this is my custom block registering, this statement will enable standard wordpress HTML anchor field (with valuable validation for spaces etc.) under Advanced tab of selected gutenberg block:
supports: {
anchor: true
}
then in the same place we define:
attributes: {
anchor: {
type: 'string'
}
}
then in save function (I have it exactly for the same purpose of InnerBlocks):
save: function(props) {
const { anchor } = props.attributes;
return (
el( anchor, {}),
el( InnerBlocks.Content, {})
);
}
if you are using jsx, the save function could look like this:
save: function(props) {
const { anchor } = props.attributes;
return (
<div id={anchor}>
<InnerBlocks.Content />
</div>
);
}
then in your render callback function (in php) it's going to be available via first arg's (which is array) element
function your_callback( $block, $content ) {
// display your anchor value
echo $block['anchor'];
}
You could use this filter (targeting whatever blocks you want)
const withAnchor = props => {
if (props.attributes) { // Some blocks don't have attributes
props.attributes = {
...props.attributes,
anchor: {
type: 'string'
}
}
}
return props
}
wp.hooks.addFilter(
'blocks.registerBlockType',
'namespace/with-anchor',
withAnchor
)
And then you can access the 'anchor' attribute in the render callback
'render_callback' => function($attributes) {
echo $attributes['anchor'];
}
Have you tried manually adding a field that will take care of the ID attribute?
Something like this:
<InspectorControls>
<PanelBody title={ __( 'Element Settings' ) }>
<TextControl
label={ __( 'Element ID', 'fleximpleblocks' ) }
value={ elementID}
placeholder={ __( 'Type in the element ID…' ) }
onChange={ ( value ) => setAttributes( { elementID: value } ) }
/>
</PanelBody>
</InspectorControls>
And then:
save: ( props ) => {
return <InnerBlocks.Content id={ props.attributes.elementID } />;
}
I'm not sure if it'll work, I'm just taking a wild guess here. Let me know how it goes :)
Based on answers above.
You just need create an attribute which collects all other attributes/variables/whatever into string.
Step 1
Create an attribute with string type(in block.json)
"phpRender": {
"type": "string"
}
Step 2
In the "edit" function of the block, create a function to save whatever you need to the attribute above. Put this function in "useEffect" hook.
const saveAllToString = () => {
const blockProps = {
id: attributes.anchor,
}
setAttributes({phpRender: JSON.stringify(blockProps)});
}
useEffect(() => {
saveAllToString();
});
Step 3
Now you can decode this string and use variables easily.
$blockProps = !empty($attributes['phpRender']) ? json_decode($attributes['phpRender']) : false;
echo $blockProps->id;

How to fix 'Error loading block: Invalid parameter(s): attributes' error in WordPress

I am attempting to extend an existing block which is registered in the Tasty Recipes plugin. I've added a control to the sidebar to handle selection of a given diet type, however when an option is selected, the following error is displayed:
Error loading block: Invalid parameter(s): attributes
Currently running on WordPress 5.2.1 and I am attempting to extend the Tasty Recipes Plugin
extendTastyRecipes.js
/**
* Add custom attribute to store diet type
*/
var addCustomAttributes = function( settings, name ) {
if ( name !== 'wp-tasty/tasty-recipe' ) {
return settings;
}
settings.attributes = Object.assign( settings.attributes, {
dietType: {
type: 'string',
// ? Setting default here causes break
},
} );
return settings;
}
wp.hooks.addFilter( 'blocks.registerBlockType', 'wp-tasty/tasty-recipe', addCustomAttributes );
/**
* Create HOC to add diet type control to inspector controls of block.
*/
var el = wp.element.createElement;
var withInspectorControls = wp.compose.createHigherOrderComponent( function( BlockEdit ) {
return function ( props ) {
function onChangeDietType (newDietType) {
props.setAttributes( { dietType: newDietType } );
}
return el(
wp.element.Fragment,
{},
el(
BlockEdit,
props
),
el(
wp.editor.InspectorControls,
{},
el(
wp.components.PanelBody,
{},
el (
wp.components.SelectControl,
{
label: 'Diet Type',
onChange: onChangeDietType,
options: [
{ label: 'Paleo', value: 'paleo' },
{ label: 'Vegan', value: 'vegan' },
{ label: 'Vegetarian', value: 'vegetarian' },
],
},
)
)
)
);
};
}, 'withInspectorControls' );
wp.hooks.addFilter( 'editor.BlockEdit', 'wp-tasty/tasty-recipe', withInspectorControls );
/**
* Not sure this is even necessary as the plugin I'm extending handles rendering on server
* -- extra --
* #param {Object} extraProps
* #param {Object} blockType
* #param {Object} attributes
*/
function addSaveProps( element ) {
return element; // This returns null
}
wp.hooks.addFilter( 'blocks.getSaveElement', 'wp-tasty/tasty-recipe', addSaveProps );
functions.php
function extendTastyRecipes() {
$blockPath = get_stylesheet_directory_uri() . '/assets/scripts/modules/extendTasyRecipes.js';
wp_enqueue_script(
'extend-tasty-recipes-js',
$blockPath,
[ 'wp-i18n', 'wp-edit-post', 'wp-element', 'wp-editor', 'wp-components', 'wp-data', 'wp-plugins', 'wp-edit-post', 'wp-api' ]
);
}
add_action('init', 'extendTastyRecipes');
Hoping I'm missing something simple... Not entirely familiar with Gutenberg so I could definitely just be misunderstanding the docs.

Wordpress Media Uploader query-attachment parameter

I'm trying to use the Media Uploader in a plugin.
I need to pass additional parameters to the query-attachment request.
I've tried to add a parameter to the Library like this:
file_frame = wp.media.frames.file_frame = wp.media({
title: "User's photo",
button: {
text: "Upload image",
},
library: {
type: ['image/jpeg','image/png'],
--> additional_param: 'a value'
},
multiple: false // Set to true to allow multiple files to be selected
});
But this additional parameter seems to screw up the new attachment upload process - the new uploaded attachment isn't display in the grid at the download end.
Is there a valid way to add a custom parameter to the query-attachment request?
Thank you for your answers
I dug a little bit un process and the best way to pass additional parameter I found is to add it during the query call.
To do it, several classes add to be extended.
Extending Query (wp.media.model.Query)
The sync Query's method is responsible to place the actual admin-ajax call to query the attachments. The sync method has 3 arguments, the last one - options - contains the parameters passed to the http request. The extended sync method adds the additional parameter before calling the parent sync request.
// Extending the wp.media.query to add a parameter
var Query1;
Query1=wp.media.model.Query1 = oldQuery.extend({
sync: function( method, model, options ) {
options = options || {};
options = _.extend(options, {'data':{'CadTest': '1'}});
return oldQuery.prototype.sync.apply(this, arguments);
}...
Now the must find a way to have this new class instancied. This done is the requery method of the Attachements class which call the 'static' ou 'class method' get of Query. In order to create a query based on Query1, the get method has had to be duplicated creating an Query1 object
get1: (function(){
/**
* #static
* #type Array
*/
var queries = [];
/**
* #returns {Query}
*/
return function( props, options ) {
var args = {},
orderby = Query1.orderby,
defaults = Query1.defaultProps,
query,
cache = !! props.cache || _.isUndefined( props.cache );
// Remove the `query` property. This isn't linked to a query,
// this *is* the query.
delete props.query;
delete props.cache;
// Fill default args.
_.defaults( props, defaults );
// Normalize the order.
props.order = props.order.toUpperCase();
if ( 'DESC' !== props.order && 'ASC' !== props.order ) {
props.order = defaults.order.toUpperCase();
}
// Ensure we have a valid orderby value.
if ( ! _.contains( orderby.allowed, props.orderby ) ) {
props.orderby = defaults.orderby;
}
_.each( [ 'include', 'exclude' ], function( prop ) {
if ( props[ prop ] && ! _.isArray( props[ prop ] ) ) {
props[ prop ] = [ props[ prop ] ];
}
} );
// Generate the query `args` object.
// Correct any differing property names.
_.each( props, function( value, prop ) {
if ( _.isNull( value ) ) {
return;
}
args[ Query1.propmap[ prop ] || prop ] = value;
});
// Fill any other default query args.
_.defaults( args, Query1.defaultArgs );
// `props.orderby` does not always map directly to `args.orderby`.
// Substitute exceptions specified in orderby.keymap.
args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
// Search the query cache for a matching query.
if ( cache ) {
query = _.find( queries, function( query ) {
return _.isEqual( query.args, args );
});
} else {
queries = [];
}
// Otherwise, create a new query and add it to the cache.
if ( ! query ) {
query = new wp.media.model.Query1( [], _.extend( options || {}, {
props: props,
args: args
} ) );
queries.push( query );
}
return query;
};
}()),
Note that get1 is a class method to create in the 2nd parameter of extend()
Extending Attachments - when the _requery method is called - in our case - the Attachments collection is replaced by a Query collection (which makes the actual admin-ajax call to query the attachments). In the extended _requery method, a Query1 query is created instead of a regular Query.
wp.media.model.Attachments1 = oldAttachments.extend({
_requery: function( refresh ) {
var props;
if ( this.props.get('query') ) {
props = this.props.toJSON();
props.cache = ( true !== refresh );
this.mirror( wp.media.model.Query1.get1( props ) );
}
},
});
Finally an object of type Attachment1 must be created during the library creation
wp.media.query1 = function( props ) {
return new wp.media.model.Attachments1( null, {
props: _.extend( _.defaults( props || {}, { orderby: 'date' } ), { query: true } )
});
};
and
// Extending the current media library frame to add a new tab
wp.media.view.MediaFrame.Post1 = oldMediaFrame.extend({
initialize: function(){
oldMediaFrame.prototype.initialize.apply(this, arguments);
var options = this.options;
this.states.add([
new Library({
id: 'inserts',
title: vja_params.title,
priority: 20,
toolbar: 'main-insert',
filterable: 'all',
multiple: false,
editable: false,
library: wp.media.query1( _.defaults({
type: 'image'
}, options.library ) ),
// Show the attachment display settings.
displaySettings: true,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: true
}),
]);
},
The additional parameter is added in the _POST array and can be used in PHP code - ie in the query_attachments_args filter
public function query_attachments_args($args){
if (isset($_POST[CadTest])) {
do whatever you want...
}
return $args;
}
I solved it with:
var post_type = 'my-cpt';
var real_ajax_url = wp.ajax.settings.url;
// For uploading.
window.wp.Uploader.defaults.multipart_params.post_type = post_type;
// For querying.
wp.ajax.settings.url = real_ajax_url + '?post_type=' + post_type;
Then listen on the wp_ajax_query-attachments action for $_GET['post_type'] and listen on the admin_init hook for 'upload-attachment' !== $_POST['action'] and $_POST['post_type'] set to 'my-cpt'.
And unset/restore the values when finished with the dialog.

Resources