magento2: loading an uicomponent with Ajax - ajax

I've been struggling with that for days...
For an admin extension, I'm trying to load an uiComponent with Ajax to be displayed in a tab. The uiComponent is correctly loaded but seems not to be fully processed by client-side knockout logic.
namespace Man4x\MondialRelay2\Block\Adminhtml\Shipping;
class Tabs
extends \Magento\Backend\Block\Widget\Tabs {
protected function _construct()
{
parent::_construct();
$this->setId('mondialrelay2_shipping_tabs');
$this->setDestElementId('container');
$this->setTitle(__('MondialRelay'));
}
protected function _beforeToHtml()
{
$this->addTab(
'mass_shipping',
[
'label' => __('Mass Shipping'),
'title' => __('Mass Shipping'),
'url' => $this->getUrl('*/*/massshipping', ['_current' => true]),
'class' => 'ajax'
]
);
return parent::_beforeToHtml();
}
}
Here is the simple controller layout:
<?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">
<container name="root" label="Root">
<uiComponent name="mondialrelay2_massshipping_grid"/>
</container>
Note: this custom uiComponent is perfectly functional when loaded in a standard (i.e. non-AJAX) way
When tracking out AJAX response, I can see the correct HTML code for the uiComponent is loaded (included the Magento specific "x-magento-init" tag). It's then processed by the jquery-ui callback:
this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
// support: jQuery <1.8
// jQuery <1.8 returns false if the request is canceled in beforeSend,
// but as of 1.8, $.ajax() always returns a jqXHR object.
if ( this.xhr && this.xhr.statusText !== "canceled" ) {
tab.addClass( "ui-tabs-loading" );
panel.attr( "aria-busy", "true" );
this.xhr
.success(function( response ) {
// support: jQuery <1.8
// http://bugs.jquery.com/ticket/11778
setTimeout(function() {
panel.html( response );
that._trigger( "load", event, eventData );
}, 1 );
})
...for loaded HTML code to be inserted in the container tag.
Then, a bunch of callbacks and hooks are processed in the js modules jungle. A "contentUpdated" event is finally triggered, causing the:
/**
* Init components inside of dynamically updated elements
*/
$('body').on('contentUpdated', function () {
if (mage) {
mage.apply();
}
});
return $.mage;
}));
mage/apply/main.js and mage/apply/scripts.js are then correctly called (as tracked down in the browser console) but... nothing happens. My loaded
<script type="x-magento-init">
disappeared but my component JS logic doesn't process at all.
Any enlightenment would be highly appreciated.
PS: after deeper investigation, it seems that component JS files of the uiComponent are actually loaded, but not their templates !

I encountered the same problem you did in a similar scenario. It seems like the bindings are not applied as they should, or at least not when they should, in this scenario. To fix it without altering the template i resorted to some additional xml, in your case this could be:
<?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">
<container name="root" label="Root" htmlTag="div" htmlId="mondialrelay2">
<uiComponent name="mondialrelay2_massshipping_grid"/>
<block class="Magento\Framework\View\Element\Text" name="ajax_ui_component_fix">
<action method="setText">
<argument name="text" xsi:type="string"><![CDATA[<script>
require(['jquery'], function ($) {
$('#mondialrelay2').applyBindings();
});
</script>]]></argument>
</action>
</block>
</container>

Related

Call view in Laravel Controller with anchor tag

I need to call a view in a Laravel Controller, with parameters and with Anchor Tag.
I have this code in my controller:
return view('plans/editPlanView',
['plan' => $plan,
'patient' => $patient,
'aliments'=>$aliments, 'menu'=>$menu, 'tabName'=>$tabName]);
But i need to add an Anchor tag to land in a specific section of the page.
I can't use
return Redirect::to(URL::previous() . "#whatever");
proposed in other posts because i need to pass some parameters.
I think there are some base problem, trying with console this:
$('html, body').animate({
scrollTop: $('#whatever').offset().top
}, 1000);
scrolling to the desired section does not work.
it seems the page makes a small snap but always returns to the top.
Update
I have found the cause of the problem. At the bottom of the blade page I have the following code, without it the anchor tag works fine. Adding it the page makes a small scroll to return to the head. I need to use the datepicker, how can I fix the problem and get the anchor tag to work?
#push('scripts')
<script type="text/javascript">
$(document).ready(function () {
$('.date').datepicker({
firstDayOfWeek: 1,
weekDayFormat: 'narrow',
inputFormat: 'd/M/y',
outputFormat: 'd/M/y',
markup: 'bootstrap4',
theme: 'bootstrap',
modal: false
});
});
</script>
#endpush
You can create the method showPage() in your contoller for example TestController
public function showPage(Request $request)
{
$plan = $request->plan;
...
return view('plans/editPlanView', [
'plan' => $plan,
'patient' => $patient,
'aliments'=>$aliments, 'menu'=>$menu, 'tabName'=>$tabName
]);
}
Then create a route for rendering that view
Route::get('/someurl', 'TestController#showPage')->name('show-page');
And then in your methods you can use something like that:
$url = URL::route('show-page', ['#whatever']);
return Redirect::to($url)
I found a workaround, I added the disable attribute to the date input, in doing so, when the datepicker is initialized, the page does not scroll up. Then, as a last javascript statement I re-enabled the fields:
$('.date').prop("disabled", false);

Add store view selector to admin toolbar in Magento 2

I am creating a module which will support different configuration settings for different store views and it would be great to have a store view selector similar to the one which appears when one is editing a product in the admin.
I have managed to add buttons to my module toolbar using the code:
class Edit extends \Magento\Backend\Block\Template
{
protected function _prepareLayout()
{
$this->getToolbar()->addChild(
'save_button',
'Magento\Backend\Block\Widget\Button',
[
'label' => __('Save'),
'data_attribute' => [
'role' => 'save',
],
'class' => 'save primary',
'onclick' => "jQuery('#mp_mymodule_edit_form').submit();",
]
);
return parent::_prepareLayout();
}
}
I wondered if it was possible to insert the store view selector using Tools::addChild method? Looked around Stack Overflow and Google in general and have not managed to find any thing on this. Fingers crossed, someone does know.
Thanks in advance
Eventually managed to work this out by poking around various Magento files, posting here in case anyone is looking for the same solution:
Method 1 - Adding to the _prapareLayout function:
$this->getToolbar()->addChild(
'store_switcher',
'Magento\Backend\Block\Store\Switcher'
);
Method 2 - Layout XML file (in my case I added this to my layout file in app/code/MP/MyModule/view/adminhtml/layout/productpricebysize_dimension_edit
<referenceContainer name="page.main.actions">
<block class="Magento\Backend\Block\Store\Switcher" name="store_switcher">
<action method="setUseConfirm">
<argument name="params" xsi:type="string">1</argument>
</action>
</block>
</referenceContainer>

Magento - add WYSIWYG editor to custom widget

I created a widget inside my custom module. Everything is working and the widget can be embedded onto CMS pages. However, instead of a textarea parameter type I want to add a WYSIWYG editor.
This is the significant part in my widget.xml:
<parameters>
<description translate="label">
<required>0</required>
<visible>1</visible>
<label>Description</label>
<type>textarea</type>
</description>
</parameters>
I wonder if there's a way to extend Magento's functionality to allow a WYSIWYG editor similar to this:
<parameters>
<description translate="label">
<required>0</required>
<visible>1</visible>
<label>Description</label>
<type>WYSIWYG</type>
</description>
</parameters>
Has anybody encountered a similar problem? .. or does anyone know how this could be achieved? Maybe through a custom renderer, which calls the WYSIWYG editor, but how..?
Thanx in advance.
I finally managed to do it myself. For all those who have the same problem, this is how I did it:
In the widget.xml I have the parameter set as follows:
<parameters>
<text translate="label">
<required>1</required>
<visible>1</visible>
<label>Specify text</label>
<description>No double quotes allowed, use single quotes instead!</description>
<type>cmswidget/widget_wysiwyg</type>
</text>
</parameters>
To enable the WYSIWYG editor on the widget's textarea, I created a new block class in my custom module, extended the class Mage_Adminhtml_Block_Widget_Form_Renderer_Fieldset_Element and overwrote the render() function:
class MyCompany_MyModule_Block_Widget_Wysiwyg extends Mage_Adminhtml_Block_Widget_Form_Renderer_Fieldset_Element
{
public function render(Varien_Data_Form_Element_Abstract $element)
{
$editor = new Varien_Data_Form_Element_Editor($element->getData());
$editor->setId($element->getId());
$editor->setForm($element->getForm());
$editor->setWysiwyg(true);
$editor->setForceLoad(true);
return parent::render($editor);
}
}
Now, I was happy to see the editor inside the widget. Unfortunately there was still a problem. Since the editor creates inline styles and attributes with double quotes and places it to the CMS page as an widget attribut - which itself is also in double quotes, the widget cannot be rendered correctly. To solve this issue I extended the class Mage_Widget_Model_Widget and replaced the editors double quotes with single quotes as follows:
class MyCompany_MyModule_Model_Widget extends Mage_Widget_Model_Widget
{
public function getWidgetDeclaration($type, $params = array(), $asIs = true)
{
if( preg_match('~(^mymodule/myblockclass)~', $type) )
{
$params['text'] = str_replace('"', "'", $params['text']);
}
return parent::getWidgetDeclaration($type, $params, $asIs);
}
}
Inside getWidgetDeclaration() function I check if the widget type is the one I want to handle. The widget type is specified in the widget.xml for each widget like here:
<MyCompany_MyModule_MyWidgetName type="mymodule/myblockclass" translate="name description" module="mymodule">
<!-- ... -->
</MyCompany_MyModule_MyWidgetName>
And now I got everything working like I wanted it to be. The editor's generated HTML will have its double quotes replaced by single quotes and the output will work perfectly. Before I escaped the double quotes like this - \"some text \". That seemed to work at first, but when editing the widget by clicking on the icon (Editor view) the html was cut off. Magento's javascript seemed to escape the strings in its own way. However, the method described above will work as I just replace double quotes by single quotes when the widget is being inserted and Magento turns the single quotes into double quotes when opening the widget in CMS Editor view.
Hope this is useful for somebody.
I don't think this is compatible with Magento 1.9 anymore.
I've tried this method and I keep getting a javascript error when saving the cms block / page where the widget is added
error: error in [unknown object].fireEvent():
event name: formSubmit
error message: Cannot set property 'value' of null
Based on Rouzbeh I added small jQuery code that validate if double quotes are used:
<description>
<![CDATA[
<script>
jQuery("#widget_options textarea").keyup(function(){
if(jQuery(this).val().indexOf('"') > -1){
jQuery(this).val(jQuery(this).val().replace('"',"'"));
alert('No double quotes allowed, use single quotes instead.')
}
});
</script>
]]>
</description>
So,the known solutions no longer seem to work on 1.9+, so I produced an alternative, which adds WYSIWYG, but using an alternative editor.
I used this editor:
https://alex-d.github.io/Trumbowyg/
with the end result looking as such:
STEP 1: Download the editor files and pace in adminhtml skin area:
In my example I had placed them in skin/adminhtml/default/js/wysiwyg
STEP 2: In your module, you need to define an admin layout update, and in your adminhtml layout file, add the directives to load the library files (and jquery)
Since I wanted this to appear only n CMS Page edits, I added via the appropriate handle:
<adminhtml_cms_page_edit>
<reference name="head">
<action method="addJs">
<script>lib/jquery/jquery-1.12.0.js</script>
</action>
<action method="addJs">
<script>lib/jquery/noconflict.js</script>
</action>
<action method="addItem"><type>skin_js</type><name>js/wysiwyg/trumbowyg.min.js</name></action>
<action method="addItem"><type>skin_css</type><name>js/wysiwyg/ui/trumbowyg.min.css</name></action>
</reference>
</adminhtml_cms_page_edit>
STEP 3: Create a new widget class to render the element:
In my example, I placed this is a module under the BLOCKS
Basically, this takes the widget xml defined element, and transposes it over to a textarea element, and then attaches the needed javascript (jquery) to initialise the wysiwyg editor.
You will see thw options being passed to the editor, defined in $options
<?php
class ProxiBlue_Landingpage_Block_Widgets_Wysiwyg extends Mage_Adminhtml_Block_Widget_Form_Renderer_Fieldset_Element
{
public function render(Varien_Data_Form_Element_Abstract $element)
{
$textarea = new Varien_Data_Form_Element_Textarea();
$textarea->setForm($element->getForm())
->setId($element->getHtmlId())
->setName($element->getName())
->setLabel($element->getLabel())
->setClass('widget-option input-area input-text');
if ($element->getRequired()) {
$textarea->addClass('required-entry');
}
if ($element->getValue()) {
$textarea->setValue($element->getValue());
}
$options = "btns: ['viewHTML', 'strong', 'em', 'del', 'undo', 'redo', 'formatting', 'superscript', 'subscript', 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull', 'unorderedList', 'orderedList', 'horizontalRule', 'fullscreen'],
semantic: true,
removeformatPasted: true,
autogrow: false";
$textarea->setData('after_element_html',
"<script>jQuery(document).ready(
function() {
jQuery(" . $element->getHtmlId() .").trumbowyg({ " . $options . " })
.on('tbwblur', function(){
var html = jQuery(this).trumbowyg('html');
html = html.replace(/\"/g, '"');
jQuery(this).trumbowyg('html', html);
});
});</script>");
$html = parent::render($textarea);
return $html;
}
}
In there you may also note this snippet:
.on('tbwblur', function(){
var html = jQuery(this).trumbowyg('html');
html = html.replace(/\"/g, '"');
jQuery(this).trumbowyg('html', html);
});
The purpose here is to changed any double quotes (") to the proper html entity of &quot
This is to prevent the storing of the textual data, in the widget params, which is encased with double quotes.
Step 4: Define the widget element:
<text_blurb translate="label">
<sort_order>50</sort_order>
<label>Textual Content</label>
<visible>1</visible>
<required>1</required>
<type>landingpage/widgets_wysiwyg</type>
</text_blurb>
Done.
Hope this is of use to someone.

parsing dojo tundra dijits in ajax response

I am having massive problems re-parsing my dojo elements in an ajax response in Zend framework. I have a simple validationTextBox and a submit button. When the index page is first rendered, all the dojo/dijit tundra styles/attributes etc are parsed correctly. However, when I have an ajax call that rebuilds the dojo form and posts it back to the Index View. The form if posted back ok but the tundra styles are no longer working. I have tried so many different things but have spent many hours getting nowhere. I thank anyone in advance who attempts to assist. I have added the simplest example to this post to explain.
layout.phtml
<?php
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo $this->doctype();
?>
<html>
<head>
<meta content="text/html; charset=UTF-8" />
<script>
function showForm()
{
dojo.xhrGet( {
url: "../public/index.php?ajax=true",
// The load function is called on successful response from server
// It inserts the response to the HTML div “put_here” placeholder
handleAs:"text",
sync:true,
load: function(response, ioArgs)
{ dojo.byId("welcome").innerHTML = response;
return response;
},
// The error function displays error message if server does not
// respond right
error: function(response, ioArgs)
{
console.error("HTTP status code: ", ioArgs.xhr.status);
return response;
}
});
// dojo.parser.parse(dojo.byId("welcome"));
}
</script>
<?php
echo $this->headTitle();
echo $this->headStyle();
echo $this->headScript();
echo $this->dojo()->setDjConfigOption('parseOnLoad', true);
echo $this->dojo()->addStyleSheetModule('dijit.themes.tundra');
echo $this->dojo();
?>
</head>
<body class='tundra'>
<div id='ajaxcontent'>
<?php echo $this->layout()->content; ?>
</div>
</body>
indexController.php
<?php
//require_once '../public/js/custom.js';
class IndexController extends Zend_Controller_Action
{
public function init()
{
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('index', 'html')
->initContext();
}
public function indexAction()
{
$form = new Zend_Dojo_Form('dojoForm', array('dojotype'=> 'dijit.layout.ContentPane'));
$element = new Zend_Dojo_Form_Element_ValidationTextBox('TestBoxName',
array(
'id' => 'TextBoxId',
'required' => true
)
);
$button = new Zend_Dojo_Form_Element_Button('TestButton',
array(
'value'=> 'Button',
'onclick' => 'showForm()'
));
$form->addElements(array($element,$button));
$this->view->form = $form;
}
}
View index.phtml
<div id="welcome" >
<?php
echo $this->form;
?>
Try to run your page through the http://validator.w3.org/ - it is probably the DOCTYPE, and issues with non-closed tags?
The line with addStyleSheetModule simply adds a css file, the class set on your <body> asserts, that all nodes below body 'inherits' the theme (CSS selectors). Selectors however is highly depending on wellconstructed DOM (hence the validation)
But the main issue here seems to be that the dijit classes are not set - probably because data-dojo-type's are not initialized. There are two approaches
1.1 In layout.phtml
function showForm() { dijit.byId('welcome').set('href', '../public/index.php?ajax=true'); }
1.2 In index.phtml
Create a contentpane (which defaults to parseOnLoad) in your <div id='welcome'..
<div id="welcome" data-dojo-type="dijit.layout.ContentPane">
2.0 call parser manually
load: function(response, ioArgs)
{ var domnode = dojo.byId("welcome");
domnode.innerHTML = response;
dojo.parser.parse(domnode);
return response;
}

Images in Magento widgets

I am developing a site for a fashion client in Magento Community version 1.4.2 and as part of this project I need to have some customized home page promotion blocks to feature specific products or categories of products. For this I thought I would write my own widget and have made pretty good progress in this with the exception of how to deal with images. I need to include an image as part of the promotion. In my widget definition file I included the following parameter
<image>
<label>Image</label>
<description>Promotion image</description>
<visible>1</visible>
<type>image</type>
</image>
This seemed at first to work fine and when creating/editing a widget in the admin back end a file upload field is included in the widget options however on saving the form the image does not appear to be uploaded, or its details retained in the database. Does anyone else have experience of using images in widgets and what I may be doing wrong?
There are a couple reason why it doesn't save/upload the image:
The form enctype needs to be "multipart/form-data" for a file upload to work
Even if you change the form enctype to "multipart/form-data" you will notice if you monitor the requests that it gets POST'ed as "application/x-www-form-urlencoded" this is because it is done through ajax and ajax by itself can't process a file upload, you need to process them separately.
I have successfully implemented a "Insert Image" button which initialises the Media Library dialogue where you can browse your server for images and/or upload images.
Once the user clicks "Insert File" it inserts the full image url into a textbox in the widget so it's passed along like a normal field to your template.
This is how I achieved it:
In your widget.xml specify a new node:
<image translate="label">
<label>Image</label>
<visible>1</visible>
<required>1</required>
<type>label</type>
<helper_block>
<type>widgets/cms_wysiwyg_images_chooser</type>
<data>
<button translate="open">
<open>Insert Image...</open>
</button>
</data>
</helper_block>
</image>
The helper block type <type>widgets/cms_wysiwyg_images_chooser</type> is a custom class, so you can change it to anything you want as long as you create the class/files correctly.
<?php
class Stackoverflow_Widgets_Block_Cms_Wysiwyg_Images_Chooser extends Mage_Adminhtml_Block_Template
{
public function prepareElementHtml(Varien_Data_Form_Element_Abstract $element)
{
$config = $this->getConfig();
$chooseButton = $this->getLayout()->createBlock('adminhtml/widget_button')
->setType('button')
->setClass('scalable btn-chooser')
->setLabel($config['button']['open'])
->setOnclick('MediabrowserUtility.openDialog(\''.$this->getUrl('*/cms_wysiwyg_images/index', array('target_element_id' => $element->getName())).'\')')
->setDisabled($element->getReadonly());
$text = new Varien_Data_Form_Element_Text();
$text->setForm($element->getForm())
->setId($element->getName())
->setName($element->getName())
->setClass('widget-option input-text');
if ($element->getRequired()) {
$text->addClass('required-entry');
}
if ($element->getValue()) {
$text->setValue($element->getValue());
}
$element->setData('after_element_html', $text->getElementHtml().$chooseButton->toHtml());
return $element;
}
}
?>
And that is it! You should now have a new field in your widget options called "Image" with a textbox and a button in which you can insert the url to an image on your server and display it from your template.
A quick explanation of how it works:
A button is created that has an onclick function that calls the Media Library dialogue by calling MediabrowserUtility.openDialog() which along is passed the parameter target_element_id which tells media library what element to set the value in once they user clicks "Insert File" so we simply pass along the id of our textbox in the widget for it to receive the url of the image that the user selected.
Hope this helps someone as I couldn't find any resources out there that explained how to do it so I spent quite a while digging through Magento to work it all out :)
What I would like to do in the future is have it so it displays the image after you select it in the widget, and it stores the url in a hidden field, but having an onchange bind is not fired when the value is set to the element from js/mage/adminhtml/browser.js in the insert function, so without changing the core files it is a lot harder to do. I thought about playing around with when the form gets focus again after the Media Library closes or a timer (pretty dodgey but would work), but I have other things to move on to and may come back to it later!
UPDATE:
The URL's that the Media Library generates are like so:
http://www.yourwebsite.com/index.php/admin/cms_wysiwyg/directive/___directive/e3ttZWRpYSB1cmw9Ind5c2l3eWcvd2lkZ2V0cy9iYW5uZXIvaG9tZXBhZ2UvZm9yZWdyb3VuZC9maXNoLXRhbmsucG5nIn19/key/e8167e3884e40b97d8985e7b84e7cbc7875f134e5f7e5946c9c2a482d0279762/
Which are a cached image, and only work if the user is an admin. Stupid? Yes. If you insert the same image in to a CMS page when the html is generated for output it converts it to the original url on the server accessible via /media/wysiwyg/path/to/file/photo.jpg. We need the original url to show to the user, so what we can do is hook into the function that generates the widget html (when you click "Insert Widget") and look for /admin/cms_wysiwyg/directive/___directive/ and replace it with the original URL to the image as the CMS page does.
In your config.xml for your custom widgets:
<global>
<models>
<widget>
<rewrite>
<widget>Stackoverflow_Widgets_Model_Widget</widget>
</rewrite>
</widget>
</models>
</global>
Then create the model Widget code\local\Stackoverflow\Widgets\Model\Widget.php:
<?php
class Stackoverflow_Widgets_Model_Widget extends Mage_Widget_Model_Widget
{
public function getWidgetDeclaration($type, $params = array(), $asIs = true)
{
foreach($params as $k => $v){
if(strpos($v,'/admin/cms_wysiwyg/directive/___directive/') !== false){
$parts = explode('/',parse_url($v, PHP_URL_PATH));
$key = array_search('___directive', $parts);
if($key !== false){
$directive = $parts[$key+1];
$src = Mage::getModel('core/email_template_filter')->filter(Mage::helper('core')->urlDecode($directive));
if(!empty($src)){
$params[$k] = parse_url($src, PHP_URL_PATH);
}
}
}
}
return parent::getWidgetDeclaration($type, $params, $asIs);
}
}
Which overrides the getWidgetDeclaration function which is called every time a the widget output is produced for a textarea/wysiwyg and looks through all the parameters and if it finds an image that is linked to the admin cache it will find out the original image and overwrite the variable in the array and call the original function with the parameters.
If the cached image is not found the function will work as normal.
UPDATE: 13/09/2012
As Jonathan Day pointed out you have to overwrite Mage_Widget_Model_Widget_Instance also if you want it to work in a Widget Instance.
I haven't had the need to add an image to a widget through a Widget Instance until now and was confused why my function didn't work, until I investigated and realised the "popup" widget instances use Mage_Widget_Model_Widget and the widget instances that are used on a Widget options tab (no popup) are Mage_Widget_Model_Widget_Instance and do not extend Mage_Widget_Model_Widget so do not inherit the functionality.
To add the functionality to Mage_Widget_Model_Widget_Instance simply add the line <widget_instance>Petbarn_Widgets_Model_Widget_Instance</widget_instance> to your config.xml so it will look like:
<global>
<models>
<widget>
<rewrite>
<widget>Stackoverflow_Widgets_Model_Widget</widget>
<widget_instance>Stackoverflow_Widgets_Model_Widget_Instance</widget_instance>
</rewrite>
</widget>
</models>
</global>
Then create the model Instance code\local\Stackoverflow\Widgets\Model\Widget\Instance.php:
<?php
class Petbarn_Widgets_Model_Widget_Instance extends Mage_Widget_Model_Widget_Instance
{
protected function _beforeSave()
{
if (is_array($this->getData('widget_parameters'))) {
$params = $this->getData('widget_parameters');
foreach($params as $k => $v){
if(strpos($v,'/cms_wysiwyg/directive/___directive/') !== false){
$parts = explode('/',parse_url($v, PHP_URL_PATH));
$key = array_search('___directive', $parts);
if($key !== false){
$directive = $parts[$key+1];
$src = Mage::getModel('core/email_template_filter')->filter(Mage::helper('core')->urlDecode($directive));
if(!empty($src)){
$params[$k] = parse_url($src, PHP_URL_PATH);
}
}
}
}
$this->setData('widget_parameters', $params);
}
return parent::_beforeSave();
}
}
This time we are modifying the widget_parameters data at the start of the _beforeSave() function so it fixes up the url before it saves it.
You also have to ensure the /js/mage/adminhtml/browser.js javascript file is included (in my case it wasn't) to get the MediabrowserUtility functionality.
To ensure it is included, the easiest way is to include it for all of admin (didn't spend much time targeting it better).
Create a local.xml for adminhtml layouts (if you don't already have one): app\design\adminhtml\default\default\layout\local.xml
<?xml version="1.0"?>
<layout version="0.1.0">
<default>
<reference name="head">
<action method="addJs"><script>mage/adminhtml/browser.js</script></action>
</reference>
</default>
</layout>
This will make Magento include js/mage/adminhtml/browser.js on every page of admin so MediabrowserUtility will always be available.
NOTE: I'm using Magento Enterprise 1.11.2.0 so I'm not sure how it behaves on other versions.
I solved this use case by creating a custom field type for widgets:
<image>
<label>Image</label>
<description>Promotion image</description>
<visible>1</visible>
<type>widgetimagechooser/chooser</type>
</image>
I implemented a block Aijko_WidgetImageChooser_Block_Chooser that triggers the standard Magento image chooser element.
To solve the problem with the non clear url to the image file I implemented a custom controller Aijko_WidgetImageChooser_Adminhtml_Cms_Wysiwyg_Images_ChooserController that handles the return value from the Magento standard image chooser.
The value is added to a textbox in the widget. This relative url to the image then can be used to show the image in the frontend.
Feel free to try the extension available on Github or install directly using Magento Connect.
Thx krus for you answer! I've found a nicer way to solve the problem with the cached image URLs. I even had to do this, because your solution overwriting the Widget Model didn't work with Magento 1.7.0.2.
So what I have done is adding a new GET parameter use_file_url to the URL used for the Chooser Block:
$url = $this->getUrl(
'*/cms_wysiwyg_images/index',
array(
'target_element_id' => $element->getName(),
'use_file_url' => 1
)
);
This passes the GET parameter to the media browser. The next step is to pass this parameter to the onInsertAction of the Mage_Adminhtml_Cms_Wysiwyg_ImagesController. Do do this, you have to override the getOnInsertUrl() function of the Mage_Adminhtml_Block_Cms_Wysiwyg_Images_Content Block:
public function getOnInsertUrl()
{
$useFileUrl = (int)$this->getRequest()->getParam('use_file_url', 0);
return $this->getUrl('*/*/onInsert', array('use_file_url' => $useFileUrl));
}
Then you need to handle the new parameter in the Mage_Adminhtml_Cms_Wysiwyg_ImagesController controller:
public function onInsertAction()
{
$useFileUrl = (int)$this->getRequest()->getParam('use_file_url', 0) == 1 ? true : false;
$helper = Mage::helper('cms/wysiwyg_images');
$storeId = $this->getRequest()->getParam('store');
$filename = $this->getRequest()->getParam('filename');
$filename = $helper->idDecode($filename);
$asIs = $this->getRequest()->getParam('as_is');
Mage::helper('catalog')->setStoreId($storeId);
$helper->setStoreId($storeId);
if ($useFileUrl == false) {
$image = $helper->getImageHtmlDeclaration($filename, $asIs);
} else {
$image = $helper->getImageMediaUrl($filename);
}
$this->getResponse()->setBody($image);
}
The last step is to override the Mage_Cms_Helper_Wysiwyg_Images helper and add the getImageMediaUrl() function:
public function getImageMediaUrl($filename)
{
return $this->getCurrentUrl() . $filename;
}
I think this is a quite pretty approach, even though you have to ovverride 4 classes. But passing a GET parameter seems to be more future safe than parsing the cached URL.
There is no file uploads for widgets. To solve that problem you may use magento media browser and select image like for wysiwyg editor, but there is other issue, media browser do not return clear url of image.
As of Magento version 1.9.2.0 you will also need to add the following to your adminhtml layout file:
lib/flex.js
lib/FABridge.js
mage/adminhtml/flexuploader.js
Magento's Image custom attributes normally does not come to extension tab and you will have to go to Catalog>Product> Images to assign this attribute. But with some custom coding you can achieve this.

Resources