Finally, I've figured out by myself, I want popup preview on grid's actions Column, so you can start by adding below code
Test/PreviewPopup/view/adminhtml/layout/previewpopup_actionslog_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<uiComponent name="actions_log_listing"/>
</referenceContainer>
<referenceContainer name="js">
<block class="Test\PreviewPopup\Block\Adminhtml\ActionsLog\Preview" template="Test_PreviewPopup::activity/js.phtml" name="activity.view.model.js"/>
</referenceContainer>
</body>
</page>
Test/PreviewPopup/Controller/Adminhtml/ActionsLog/Index.php
<?php
namespace Test\PreviewPopup\Controller\Adminhtml\ActionsLog;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Backend\App\Action;
use Magento\Backend\Model\UrlInterface;
class Index extends Action
{
/**
* @var string
*/
const ADMIN_RESOURCE = 'Test_PreviewPopup::actions_log';
/**
* @var PageFactory
*/
public $resultPageFactory;
/**
* @var \Magento\Backend\Model\UrlInterface
*/
protected $backendUrl;
/**
* Index constructor.
* @param Context $context
* @param PageFactory $resultPageFactory
*/
public function __construct(
Context $context,
PageFactory $resultPageFactory,
UrlInterface $backendUrl
) {
$this->resultPageFactory = $resultPageFactory;
$this->backendUrl = $backendUrl;
parent::__construct($context);
}
/**
* Index action
* @return \Magento\Backend\Model\View\Result\Page
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$resultPage->setActiveMenu('Test_PreviewPopup::actions_log');
$resultPage->addBreadcrumb(__('Test'), __('Actions Log'));
$resultPage->getConfig()->getTitle()->prepend(__('Actions Log'));
return $resultPage;
}
/**
* access rights checking
*
* @return bool
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Test_PreviewPopup::actions_log');
}
}
Test/PreviewPopup/view/adminhtml/ui_component/actions_log_listing.xml
<actionsColumn name="actions" class="Test\PreviewPopup\Ui\Component\Listing\Columns\LogActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="bodyTmpl" xsi:type="string">ui/grid/cells/html</item>
<item name="viewUrlPath" xsi:type="string">previewpopup/actionslog/preview</item>
<item name="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">107</item>
<item name="indexField" xsi:type="string">actions_id</item>
<item name="urlEntityParamName" xsi:type="string">actions_id</item>
</item>
</argument>
</actionsColumn>
Test/PreviewPopup/Ui/Component/Listing/Columns/LogActions.php
<?php
namespace Test\PreviewPopup\Ui\Component\Listing\Columns;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\UrlInterface;
class LogActions extends \Magento\Ui\Component\Listing\Columns\Column
{
protected $urlBuilder;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
UrlInterface $urlBuilder,
\Magento\Framework\View\LayoutInterface $layout,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
$this->layout = $layout;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Get item url
* @return string
*/
public function getViewUrl()
{
return $this->urlBuilder->getUrl(
$this->getData('config/viewUrlPath')
);
}
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as &$item) {
if (isset($item['actions_id'])) {
$name = $this->getData('name');
$item[$name] = html_entity_decode('<a href="#" class="action-activity-log-view"
onclick="adminActivityLogView.open(\''
. $this->getViewUrl() . '\', \'' .
$item['actions_id']. '\', \'' .
$item['restore'] . '\')'
.'")>'. __('Preview Changes').'</a>
<br/>
<a href="'.$this->urlBuilder->getUrl(
'previewpopup/actionslog/edit',
['actions_id' => $item['actions_id']]
).'">'. __('View Details').'</a>');
//View Details is not needed i've just added according to my requirement
}
}
}
return $dataSource;
}
}
Test/PreviewPopup/view/adminhtml/templates/activity/js.phtml
<script>
require([
"jquery",
"Magento_Ui/js/modal/modal",
'mage/backend/notification',
"prototype"
], function(jQuery, modal, notification) {
//<![CDATA[
Window.keepMultiModalWindow = true;
var adminActivityLogView = {
overlayShowEffectOptions : null,
overlayHideEffectOptions : null,
modal: null,
activityId: 0,
open : function(editorUrl, elementId, revertable) {
if (editorUrl && elementId) {
jQuery.ajax({
url: editorUrl,
data: {
id: elementId
},
showLoader: true,
dataType: 'html',
success: function(data, textStatus, transport) {
this.openDialogWindow(data, elementId, revertable);
}.bind(this)
});
}
},
openDialogWindow : function(data, elementId, revertable) {
var self = this;
this.activityId = elementId;
if (this.modal) {
this.modal.html(jQuery(data).html());
} else {
this.modal = jQuery(data).modal({
title: '<?= /* @escapeNotVerified */ __('Actions Log'); ?>',
modalClass: 'magento',
type: 'slide',
firedElementId: elementId,
buttons: [{
text: jQuery.mage.__('Back'),
class: 'action- scalable back',
click: function () {
self.closeDialogWindow(this);
}
}],
close: function () {
self.closeDialogWindow(this);
}
});
}
jQuery(".action-revert-activity").hide();
if(revertable == 1) {
jQuery(".action-revert-activity").show();
}
this.modal.modal('openModal');
this.autoResize();
},
closeDialogWindow : function(dialogWindow) {
jQuery('body').trigger('processStop');
dialogWindow.closeModal();
Windows.overlayShowEffectOptions = this.overlayShowEffectOptions;
Windows.overlayHideEffectOptions = this.overlayHideEffectOptions;
},
error: function (message) {
jQuery('body').notification('clear')
.notification('add', {
error: true,
message: jQuery.mage.__(message),
insertMethod: function (message) {
var $wrapper = jQuery('<div/>').html(message);
jQuery('.page-main-actions').after($wrapper);
}
});
},
autoResize: function () {
jQuery.each(jQuery('textarea.value-container'), function() {
var offset = this.offsetHeight - this.clientHeight;
var resizeTextarea = function(el) {
console.log('You clicked me!!');
jQuery(el).css('height', 'auto').css('height', el.scrollHeight + offset);
};
jQuery(this).unbind().on('click', function() { resizeTextarea(this); }).trigger('click');
});
}
};
window.adminActivityLogView = adminActivityLogView;
//]]>
});
</script>
Test/Previewpopup/Controller/Adminhtml/ActionsLog/Preview.php;
<?php
namespace Test\Previewpopup\Controller\Adminhtml\ActionsLog;
use Magento\Backend\App\Action\Context;
use Magento\Backend\App\Action;
class Preview extends Action
{
/**
* @var \Magento\Framework\Controller\Result\RawFactory
*/
public $resultRawFactory;
/**
* @var \Magento\Framework\View\LayoutFactory
*/
public $layoutFactory;
/**
* Log constructor.
* @param Context $context
* @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory
* @param \Magento\Framework\View\LayoutFactory $layoutFactory
*/
public function __construct(
Context $context,
\Magento\Framework\Controller\Result\RawFactory $resultRawFactory,
\Magento\Framework\View\LayoutFactory $layoutFactory
) {
$this->resultRawFactory = $resultRawFactory;
$this->layoutFactory = $layoutFactory;
parent::__construct($context);
}
/**
* view action
* @return $this
*/
public function execute()
{
$content = $this->layoutFactory->create()
->createBlock(
\Test\PreviewPopup\Block\Adminhtml\ActionsLog\Preview::class
);
/** @var \Magento\Framework\Controller\Result\Raw $resultRaw */
$resultRaw = $this->resultRawFactory->create();
return $resultRaw->setContents($content->toHtml());
}
}
Test/PreviewPopup/Block/Adminhtml/ActionsLog/Preview.php
<?php
namespace Test\PreviewPopup\Block\Adminhtml\ActionsLog;
use Magento\Backend\Block\Template;
class Preview extends Template
{
/**
* Path to template file in theme.
* @var string
*/
public $_template = 'Test_PreviewPopup::tab/modification.phtml';
/**
* Preview constructor.
* @param Template\Context $context
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Backend\Model\Auth\Session $authSession,
\Test\PreviewPopup\Model\ActionsLogFactory $actionlogFactory,
\Test\PreviewPopup\Model\ResourceModel\ActionsLogChanges\CollectionFactory $actionsLogChangesFactory,
array $data = []
) {
$this->authSession = $authSession;
$this->actionlogFactory = $actionlogFactory;
$this->actionsLogChangesFactory = $actionsLogChangesFactory;
parent::__construct($context, $data);
}
public function getModificationDetails()
{
$id = $this->getActionsId();
$actionsLogChangescollections = $this->actionsLogChangesFactory
->create()
->addFieldToFilter(
'actions_id',
array('eq' => $id)
)
->load();
return $actionsLogChangescollections;
}
public function getActionsId()
{
return $this->getRequest()->getParam('id');
}
public function getUserEmail()
{
return $this->authSession->getUser()->getEmail();
}
/**
* Check permission for passed action
*
* @param string $resourceId
* @return bool
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed("Test_PreviewPopup::actions_log");
}
}
Test/PreviewPopup/view/adminhtml/templates/tab/modification.phtml
<?php
$collection = $block->getModificationDetails();
// I wanted to show custom collection Data in grid so i've added this code you can make changes according to your requirements.
if($collection->getSize() > 0)
{
?>
<div class="grid">
<div class="admin__table-wrapper">
<table cellspacing="0" class="data-grid">
<thead>
<tr class="headings">
<th class="data-grid-th">Name</th>
<th class="data-grid-th">Old Value</th>
<th class="data-grid-th">New Value</th>
</tr>
</thead>
<tbody>
<tr class="data-grid-controls-row data-row ">
<td align="center" colspan="3" class="data-grid-checkbox-cell ammodel">Magento\User\Model\User\Interceptor</td>
</tr>
<?php foreach ($collection as $collections) {
?>
<tr class="data-grid-controls-row data-row _odd-row">
<td><?= $collections->getName(); ?></td>
<td class="onlyDeletions">
<pre>
<ins><?= $collections->getNewValue(); ?></ins>
<del><?= $collections->getOldValue(); ?></del>
</pre>
</td>
<td class="onlyInsertions">
<pre>
<ins><?= $collections->getNewValue(); ?></ins>
<del><?= $collections->getOldValue(); ?></del>
</pre>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
<?php } else { ?>
<div class="grid">
<div class="admin__table-wrapper">
<table cellspacing="0" class="data-grid">
<thead>
<tr class="headings">
<th class="data-grid-th">Name</th>
<th class="data-grid-th">Old Value</th>
<th class="data-grid-th">New Value</th>
</tr>
</thead>
<tbody>
<tr class="data-grid-controls-row data-row _odd-row">
<td align="center" colspan="3" class="dashboard-advanced-reports-content">We couldn't find any records.</td>
</tr>
</tbody>
</table>
</div>
</div>
<?php } ?>
Hope it Helps.
To change All the Images to External Images Please follow below steps and create one module for that.
To change all Images in front-end you need to create four plugin for that to modify and change images with your custom external images.
First Create di.xml file
<VendorName>/<ModuleName>/etc/frontend/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Block\Product\View\Gallery">
<plugin name="add_images_to_gallery" type="<VendorName>\<ModuleName>\Plugin\AddImagesToGalleryBlock" />
</type>
<type name="Magento\Catalog\Block\Product\AbstractProduct">
<plugin name="after_get_image_plugin" type="<VendorName>\<ModuleName>\Plugin\AfterGetImage"/>
</type>
<type name="Magento\Catalog\Block\Product\Image">
<plugin name="after_get_image_url_plugin" type="<VendorName>\<ModuleName>\Plugin\AfterGetImageUrl"/>
</type>
<type name="Magento\Checkout\CustomerData\AbstractItem">
<plugin name="minicart_after_get_itemdata_plugin" type="<VendorName>\<ModuleName>\Plugin\Minicart\AfterGetItemData"/>
</type>
</config>
Now You need to create a plugin files.
<VendorName>/<ModuleName>/Plugin/AddImagesToGalleryBlock.php
<?php
namespace <VendorName>\<ModuleName>\Plugin;
use Magento\Catalog\Block\Product\View\Gallery;
use Magento\Framework\Data\Collection;
use Magento\Framework\Data\CollectionFactory;
use Magento\Framework\DataObject;
class AddImagesToGalleryBlock
{
/**
* @var CollectionFactory
*/
protected $dataCollectionFactory;
/**
* AddImagesToGalleryBlock constructor.
*
* @param CollectionFactory $dataCollectionFactory
*/
public function __construct(
CollectionFactory $dataCollectionFactory
) {
$this->dataCollectionFactory = $dataCollectionFactory;
}
/**
* afterGalleryImages Plugin to change images and use external images stored in custom attribute
*
* @param Gallery $subject
* @param Collection|null $images
* @return Collection|null
*/
public function afterGetGalleryImages(Gallery $subject, $images) {
try {
$hasExternalImage = false;
// logic to get your external images url
if (!$hasExternalImage) {
return $images;
}
$product = $subject->getProduct();
$images = $this->dataCollectionFactory->create();
$productName = $product->getName();
$externalImages = ["https://static.integromat.com/img/packages/magento_256.png"]; // Array of images
foreach ($externalImages as $item) {
$imageId = uniqid();
$small = $item;
$medium = $item;
$large = $item;
$image = [
'file' => $large,
'media_type' => 'image',
'value_id' => $imageId, // unique value
'row_id' => $imageId, // unique value
'label' => $productName,
'label_default' => $productName,
'position' => 100,
'position_default' => 100,
'disabled' => 0,
'url' => $large,
'path' => '',
'small_image_url' => $small,
'medium_image_url' => $medium,
'large_image_url' => $large
];
$images->addItem(new DataObject($image));
}
return $images;
} catch (\Exception $e) {
return $images;
}
}
}
<VendorName>/<ModuleName>/Plugin/AfterGetImage.php
<?php
namespace <vendorName>\<ModuleName>\Plugin;
use Magento\Catalog\Block\Product\AbstractProduct;
class AfterGetImage
{
/**
* AfterGetImage constructor.
*/
public function __construct()
{
}
/**
* @param AbstractProduct $subject
* @param $result
* @param $product
* @param $imageId
* @param $attributes
* @return mixed
*/
public function afterGetImage(AbstractProduct $subject, $result, $product, $imageId, $attributes) {
try {
if ($product) {
$image = array();
$image['image_url'] = "https://static.integromat.com/img/packages/magento_256.png";
$image['width'] = "240";
$image['height'] = "300";
$image['label'] = $product->getName();
$image['ratio'] = "1.25";
$image['custom_attributes'] = "";
$image['resized_image_width'] = "399";
$image['resized_image_height'] = "399";
$image['product_id'] = $product->getId();
if ($image) {
$result->setData($image);
}
}
} catch (\Exception $e) {
}
return $result;
}
}
<VendorName>\<ModuleName>\Plugin\AfterGetImageUrl.php
<?php
namespace <venfdorName>\<moduleName>\Plugin;
use Magento\Catalog\Block\Product\Image;
class AfterGetImageUrl
{
/**
* AfterGetImage constructor.
*/
public function __construct(
)
{
}
/**
* @param Image $image
* @param $method
* @return array|null
*/
public function after__call(Image $image, $result, $method)
{
try {
if ($method == 'getImageUrl' && $image->getProductId() > 0) {
$result = "https://static.integromat.com/img/packages/magento_256.png";
}
} catch (\Exception $e) {
}
return $result;
}
}
<VendorName>\<ModuleName>\Plugin\Minicart\AfterGetItemData.php
<?php
namespace <vendorName>\<moduleName>\Plugin\Minicart;
use Magento\Checkout\CustomerData\AbstractItem;
class AfterGetItemData
{
/**
* AfterGetImageData constructor.
*/
public function __construct(
)
{
}
/**
* @param AbstractItem $item
* @param $result
* @return mixed
*/
public function afterGetItemData(AbstractItem $item, $result)
{
try {
if ($result['product_id'] > 0) {
$image = "https://static.integromat.com/img/packages/magento_256.png";
$result['product_image']['src'] = $image;
}
} catch (\Exception $e) {
}
return $result;
}
}
Thats all changes for Change images in storefront for product on all the places.
I hope it will help you.
Best Answer
I put this code inside a theme, but the concept can be used inside any module with basically the same file paths.
First create a template file. This will hold the content on the page that will call for the modal, the content of the modal itself and the JS that will make the connection between inside the template and the JS file that will drive the modal logic.
Note, that i'm using the built in system to pass data from the template to inside the JS file, this helps make the file more modular, you can use the same JS file to drive many modals while just changing out the content in the template file.
Then create the JS file.
There is a lot going on here, and i can't cover all of it. But the file pulls in the core JS modal with
Magento_Ui/js/modal/modal
and you then configure it with_getModalOptions
. You can find all the options for the widget in the core file, this is just what i needed to get this running. You then pass this function to the_create
constructor withthis.options.modalOption = this._getModalOptions()
. Next you trigger the_bind
which executes the actual modal. I think there is most likely a better way to do this outside using a$(document).on('click'
but this was working solid for me.Hope this helps.