In WordPress it is easy to get an images srcset and sizes as saved in WordPress using the wp_get_attachment_image_srcset function. I need to have that functionality on the block-editors side in javascript / react. Is that possible?
What I expect is a function that accepts a media id and returns the srcset somehow. If that is not possible, how could I implement a rest api endpoint for doing that? Or is there another way to pipe the function values from php to js?
For some reasons which are not relevant here, I cannot use dynamic php blocks.
I've share my solution here because I've see this post by searshing a solution for a similar problem : I was looking for a solution to get the srcset attribute and the complete generated 'img' tag but from the 'media' object returned by MediaReplaceFlow component (not only by attachment ID). It's probably adaptable to your problem by getting the media object with wp.data.select( 'core').getMedia(media_id).
This media object already contain a 'sizes' array with URL of image in some image sizes. but it's not sufficient because it's hard to build srcset ans size attribute for image tag. So, after many research, I've found a very helpfull php filter : wp_prepare_attachment_for_js
so with this filter, I've writed this PHP code :
function imageTagForJs( $response, $attachment ) {
foreach ( $response['sizes'] as $size => $datas ) {
$response['sizes'][$size]['tag'] = wp_get_attachment_image( $attachment->ID, $size );
$response['sizes'][$size]['srcset'] = wp_get_attachment_image_srcset( $attachment->ID, $size );
}
return $response;
}
add_filter( 'wp_prepare_attachment_for_js', 'imageTagForJs', 10, 2 );
Now, in the 'media' JS object, each sizes get two new properties : tag and srcset. I can now get my img tag in js by simply using media.sizes.medium.tag.
Important : By default, media.sizes array contain only builtin image sizes (thumbnail, medium, etc) but not custom image sizes. You can simply add custom sizes by using image_size_names_choose filter.
You can create an AJAX request that will be calling the wp_get_attachment_image_srcset on an attachment ID and return the result.
This example uses jQuery from the default "Twenty Seventeen" theme
Inside the child theme functions.php we define the AJAX WP method:
add_action("wp_ajax_get_attachment_image_srcset", "get_attachment_image_srcset");
add_action("wp_ajax_nopriv_get_attachment_image_srcset", "get_attachment_image_srcset");
function get_attachment_image_srcset() {
// Get attachment ID from the request
$attachment_id = $_REQUEST['attachment_id'];
// Get attachment srcset
$srcset = wp_get_attachment_image_srcset($attachment_id);
// Return JSON with the srcset and the attachment_id
wp_send_json([
'srcset' => $srcset,
'attachment_id' => $attachment_id
]);
}
Inside Block Editor, you can add a custom HTML block using AJAX to fetch the attachment srcset using an attachment ID.
Block Editor custom HTML code block:
jQuery(function($) {
// On a button click
$('.get-srcset a').on('click', function() {
// Get the value of a text input to pass as attachment ID
let attachmentId = $('#attachmentid').val();
// Make the AJAX call
jQuery.ajax({
type : "post",
dataType : "json",
url : '/wp-admin/admin-ajax.php',
data : {
action: "get_attachment_image_srcset",
attachment_id : attachmentId
},
success: function(response) {
// If we got an srcset
if(response.srcset) {
// Update the result element text with trhe srcset value or use srcset
$('#result').text(response.srcset);
}
else {
alert("No srcset")
}
}
});
});
});
This will update the #result element text to something like this:
https://wp.lytrax.net/wp-content/uploads/2020/04/3_ser_conv07-300x225.jpg 300w,
https://wp.lytrax.net/wp-content/uploads/2020/04/3_ser_conv07-768x576.jpg 768w,
https://wp.lytrax.net/wp-content/uploads/2020/04/3_ser_conv07.jpg 832w
You can check this working on this demo WP site: https://wp.lytrax.net/test-attachment-srcset/ (Valid attachment IDs are 5, 6, 7, 8, 9, 10)
Im using this
import { get } from 'lodash';
import { select, useSelect } from '#wordpress/data';
const image = useSelect( () => select( 'core' ).getMedia( id ) );
and lodash
const thumbnail = get( image, [ 'media_details', 'sizes', 'medium', 'source_url' ] );
For more: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/image/image.js#L90
TO get the featured image first you need featured image id and then pass it to the getmedia function to get the media object. after getting media object you can get relevant items from the object.
const featuredImageId = wp.data.select( 'core/editor' )
.getEditedPostAttribute( 'featured_media' );
const media = featuredImageId
? wp.data.select( 'core').getMedia( featuredImageId )
: null;
console.log(media);
Related
I'm trying to make a javascript function that when called, will take user input from a textbox and search google images with that input and get the link from the first image that comes up and then set it as the source for an image (I have not decided on the id for the image yet), any suggestions on how to do this would be much appricated
You can do it easy with this Node library: https://www.npmjs.com/package/image-search-google
Here is an example of the JS code:
const imageSearch = require('image-search-google');
const client = new imageSearch('CSE ID', 'API KEY');
const options = {page:1};
client.search('APJ Abdul kalam', options)
.then(images => {
/*
[{
'url': item.link,
'thumbnail':item.image.thumbnailLink,
'snippet':item.title,
'context': item.image.contextLink
}]
*/
})
.catch(error => console.log(error););
// search for certain size
client.search('Mahatma Gandhi', {size: 'large'});
// search for certain type
client.search('Indira Gandhi', {type: 'face'});
With the atribute url, you can generate an a tag with this URL.
I use xwiki to create a wiki. I want to dynamicaly display a list of pages (in a livetable or just bulk.), so I use the tag system.
Currently I use the HTML macro + iframe but it display all the page with header, side menus, options, etc.
I've tried this snippet but nothing is displaying and I don't really understand all the code, I'm not sure that is the good solution.
I've tried to use Display and Include macros :
{{display reference="Main.Tags"/}}
It display all my tags in a cloud.
But to had what I want I should specify this code with
queryString="do=viewTag&tag=Test"
Or something like that but I don't know how to do it.
So if you have an idea to display the list of pages with the same tag I will be happy to read it :)
Thanks.
EDIT1
So I work on it and I display what i want with the following instruction :
{{velocity}}
#set ($list = $xwiki.tag.getDocumentsWithTag('test'))
#foreach($doc in $list)
$doc
#end
{{/velocity}}
But the problem is that display all the path of the document.
Wiki Interne.2\. Liste des flux TEST.2_1_Flux_Externes_Entrants.AGDAT01.WebHome
Wiki Interne.2\. Liste des flux TEST.2_1_Flux_Externes_Entrants.AGOL20.WebHome
Wiki Interne.2\. Liste des flux TEST.2_1_Flux_Externes_Entrants.AGOL21.WebHome
Wiki Interne.2\. Liste des flux TEST.2_1_Flux_Externes_Entrants.AGOL22.WebHome
How can I restrict the display only to the title of the document?
You can use the Livetable macro (http://extensions.xwiki.org/xwiki/bin/view/Extension/Livetable%20Macro) to list the pages you want and customize it to only show the pages tagged with a specific tag.
{{velocity}}
#set($collist = ['doc.title', 'doc.location', 'doc.date', 'doc.author'])
#set($colprops = {
'doc.title' : { 'size' : 30, 'link' : 'view', 'filterable': false, 'sortable': false },
'doc.location' : { 'html' : true },
'doc.date' : { 'type' : 'date' },
'doc.author' : { 'type' : 'text', 'link' : 'author' }
})
#set($options = {
'translationPrefix' : 'platform.index.',
'rowCount' : 15,
'tagCloud' : 'true',
'selectedTags' : ['test']
})
#if(!$isGuest)
#set($discard = $collist.add('_actions'))
#set($discard = $colprops.put('_actions', { 'actions' : ['copy', 'delete', 'rename', 'rights'] }))
#end
#livetable('taggedDocs' $collist $colprops $options)
{{/velocity}}
Since the 'tagCloud' option needs to be enabled in order for the 'selectedTags' option to allow you to list the tags you want to show by default (in my example, I am listing pages tagged with the tag 'test'), you will also see all the other available tags that the user could select from. If that bothers you and you want to not allow the user to change the displayed tag, you could simply hide the 'tagCloud' section above the livetable by going in object edit mode on the page and adding an object of type 'StyleSheetExtension' (see http://www.xwiki.org/xwiki/bin/view/Documentation/DevGuide/Tutorials/SkinExtensionsTutorial/#HMinimalStyleSheeteXtension for more details) with the following CSS content:
#alldocs-tagcloud {
display : none;
}
Thanks #Eduard Moraru :) I will try it.
However I've resolved my case when I found the Velocity doc : http://velocity.apache.org/engine/1.7/user-guide.html
The code for people who might be interested :
{{velocity}}
#set ($list = $xwiki.tag.getDocumentsWithTag('Your Tag'))
#foreach($reference in $list)
#set ($document = $xwiki.getDocument($reference))
#set ($label = $document.getTitle())
[[$label>>$reference]]
#end
{{/velocity}}
Try this, it is shameless cut fron build-in tag app.
{{velocity}}
#set ($tag='Tag')
#if ("$!{request.get('renamedTag')}" != '')
{{info}}$services.localization.render('xe.tag.rename.success', ["//${request.get('renamedTag')}//"]){{/info}}
#end
#set ($list = $xwiki.tag.getDocumentsWithTag($tag))
{{container layoutStyle="columns"}}
(((
(% class="xapp" %)
=== $services.localization.render('xe.tag.alldocs', ["//${tag}//"]) ===
#if ($list.size()> 0)
{{html}}#displayDocumentList($list false $blacklistedSpaces){{/html}}
#else
(% class='noitems' %)$services.localization.render('xe.tag.notags')
#end
)))
{{/container}}
#set ($displayDocExtra = false)
{{/velocity}}
For My projet i need to know how i can transmit some variables to a async url. The plugin I use is a simple popover that call a URL to fetch html data and show the result.
I need to use $(this) because i have many URls with the same class. I must transmit the data-type (ex: picture) and the id of the product (data-id).
My link = Check this out
What i want to do (but it doesn't work) :
$('.product').webuiPopover({
var productType = $(this).data('type');
var id = $(this).data('id');
type:'async',
url:'/api/popover/'+ productType +'/'+id
});
Is it possible ? How i can do that ?
Note : here is the plugin I use (github : sandywalker/webui-popover)
You have to initialize webuiPopover for each .product element individually. Do something like this:
$('div').each(function(index, el){
$(el).webuiPopover({
title: $(el).data('foo') // Direct access to the data attributes of this element
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/jquery.webui-popover/1.2.5/jquery.webui-popover.min.js"></script>
<div data-foo="First title">Hello</div>
<div data-foo="Second title">Goodbye</div>
If you want to add variable then you can do it like:
$('.product').each(function(i,t){
var t = $(t);
t.webuiPopover({
type:'async',
url:'/api/popover/'+ t.data('type') +'/'+ t.data('id')
});
});
On my magento product page I need to add a dynamic JavaScript array base on display upselling products on the product page. The goal is to change the images of the upselling products when the user change the color of the main product.
To achieve my goal I need a custom JavaScript array on every product page that give me information about crossselling product and the associated product image.
What is the best way to do this?
I try this
add a observer event in my config.xml
<controller_action_layout_load_before>
<observers>
<crossselling_product_view>
<type>singleton</type>
<class>XXXXXXXX_Crossselling_Model_Observer</class>
<method>productview</method>
</crossselling_product_view>
</observers>
</controller_action_layout_load_before>
add observer to add specific JS Code
<?php
class XXXXXXXX_Crossselling_Model_Observer {
public function productview(Varien_Event_Observer $observer) {
$product = Mage::registry('current_product');
//only on product page
if (!($product instanceof Mage_Catalog_Model_Product)) {
return;
}
$controller = $observer->getAction();
$layout = $controller->getLayout();
$block = $layout->createBlock('core/text');
$block->setText(
'<script type="text/javascript">
function main_pulsestorm_hellojavascript()
{
alert("Foo");
}
main_pulsestorm_hellojavascript();
</script>'
);
$layout->getBlock('head')->append($block);
}
}
My error:
Fatal error: Call to a member function append() on a non-object
What is my problem and is it the right way to add dynamic js code?
I would probably approach this from a different angle. Since you are only interested in interacting with data and output for the upsell block, you could change the behavior of just that block by observing its output and appending your extra JavaScript. For the purposes of brevity this answer assumes that you understand the basics of Magento extensions.
Observe the core_block_abstract_to_html_after event:
etc/config.xml
<core_block_abstract_to_html_after>
<observers>
<addCustomUpsellFormat>
<class>XXXXXXXX_Crossselling_Model_Observer</class>
<method>addCustomUpsellFormat</method>
</addCustomUpsellFormat>
</observers>
</core_block_abstract_to_html_after>
Act upon instances of Mage_Catalog_Block_Product_List_Upsell by appending the output of a new block that will read their data:
Model/Observer.php
public function addCustomUpsellFormat(Varien_Event_Observer $observer)
{
/* #var Mage_Core_Block_Abstract $block */
$block = $observer->getBlock();
if ($block instanceof Mage_Catalog_Block_Product_List_Upsell) {
/* #var Varien_Object $transport */
$transport = $observer->getTransport();
// Receive the standard output for the block.
$output = $transport->getHtml();
/* #var Mage_Core_Model_Layout $layout */
$layout = $block->getLayout();
$json = $layout->createBlock('core/template')
->setTemplate('catalog/product/list/upsell_json.phtml')
->setItems($block->getItems())
->toHtml();
// Append new JSON data to block output.
$transport->setHtml($output . $json);
}
return $this;
}
Create a template that interprets the upsell data and outputs it in your desired way, in my example above I created a template that could do something like this (my example creates a new template, so it should go into the base theme):
app/design/frontend/base/default/template/catalog/product/list/upsell_json.phtml
<?php
$_json = array(); // Add data in here to convert to JSON for output.
$_items = $this->getItems();
/* #var Mage_Catalog_Model_Product $_product */
foreach ($_items as $_product) {
$_json[$_product->getId()] = array(
'image' => (string)Mage::helper('catalog/image')->init($_product, 'image')
);
}
?>
<script type="text/javascript">var upsellData = <?php echo json_encode($_json) ?>;</script>
Use
$controller = $observer->getEvent()->getAction();
instead of
$controller = $observer->getAction();
I have created a dynamic table. DEMO to my project.
I have placed this dynamic table in the form under a div named 'box'
<div id="box">
</div>.
I am creating dynamic hidden variables table using Jquery which I need to store in DB. This is how I am creating the hash to submit to server.
criteria = $('form_name').serialize(true);
criteria = Object.toJSON(criteria);
// Build the object to store the parameters for the AJAX post request
parameters = {
title : $('report_title').value,
description : $('report_description').value,
criteria : criteria
}
// Make the AJAX post request
new Ajax.Request( URL, {
method: 'post',
parameters: parameters,
onSuccess: function( response ) {
$('messageSpan').innerHTML = response.responseText;
$('spinner').style.display='none';
}
});
I am not able to capture the dynamically created values in the criteria.
How to solve this?
In the dynamically created section, I tried adding a submit button and see if the values can be fetched. I am able to fetch and iterate all the hidden variables.
$('#jquerysaveButton').click(function(){
jsonObj = [];
$("input[id=rubric_cell]").each(function () {
var id = "cell_" + row + "_" + col;
item = {}
item["id"] = id;
item["selected_rubric"] = $(this).val();
jsonObj.push(item);
});
console.log(jsonObj); //I am getting the required values here
});
How to get these values in the criteria = $('form_name').serialize(true);. Am I doing some thing wrong? Please help me. thanks in advance.
DEMO to my project
You need to make sure that your hidden input fields have a name attribute set otherwise $.serialize will not process them.