How to add promotional labels on top of images in Magento 2 ? - magento

How can I place a promotional label layer on top of images in Magento 2. The image I am including is from the out of the box Magento 2 theme and it has a text " New Luma Yoga Collection ...." and a button "Shop New Yoga" that was somehow placed on top of the image in the editor .
This is how it looks in the editor

There are various ways on how you can achieve this. One way is to add a HTML element directly into the WYSIWYG editor and mark it up with HTML. This is also how the Luma theme has done this. If you look at your editor, you see the content in small blue below the image. If you switch from WYSIWYG to HTML, you can see the HTML markup of this element:
<span class="content bg-white">
<span class="info">New Luma Yoga Collection</span>
<strong class="title">Get fit and look fab in new seasonal styles</strong>
<span class="action more button">Shop New Yoga</span>
</span>
You can simply use CSS to style this element.
However...
Although this is a very widely uses approach to do such a thing (and it's easy and fast to do so), it's not the most elegant solution. After all, it's not clear from the WYSIWYG-editor that the 'blue link' is actually a special element. If your client starts messing with it, he will break the layout and call you because 'you made the site, so it's your fault'. Trust me, I've been there...
A more elegant solution would be to use widgets. A widget is very simple to create in Magento 2. First you have to create a file called widget.xml in the etc-folder of your module, and put something like this in it:
<?xml version="1.0"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
<widget id="example_widget" class="Vendor\Module\Block\Widget\Example">
<label translate="true">Example widget</label>
<description translate="true">This is an example widget</description>
</widget>
</widgets>
Now you can create a Block in the Block-folder of your module:
use Magento\Framework\View\Element\Template;
use Magento\Widget\Block\BlockInterface;
class Example extends Template implements BlockInterface
{
/**
* #return string
*/
public function _toHtml()
{
return '<p class="hello">Hello world!</p>';
}
}
Now if you click the widget-button in the WYSIWYG-editor, the widget will be there in the list of widgets to choose from:
Now, if you insert this widget in your WYSIWYG editor, you're sure about the HTML it will output (since that is handled with PHP), and your client cannot 'break it'.
In your scenario you most likely want to add parameters so you can make your widget reusable. This is very simple. Edit your widget.xml:
<widget id="example_widget" class="Vendor\Module\Block\Widget\Example">
<label translate="true">Example widget</label>
<description translate="true">This is an example widget</description>
<parameters>
<parameter name="name" xsi:type="text" visible="true" sort_order="0">
<label translate="true">Name</label>
<description translate="true">Please enter a name</description>
</parameter>
</parameters>
</widget>
And to use it in your Block Class:
public function _toHtml()
{
return '<p class="hello">Hello ' . $this->getName() . '</p>';
}
It's really that simple.
In your specific case I would suggest creating a widget with 4 parameters:
Header
Content
Button Text
Button Link

Related

TinyMCE 5 extended_valid_elements

In input HTML I have the element:
<img id="p_W_1" name="{1234}" class="po" src="../tinymce/plugins/myplugin/img/icon.png" data-mce-src="../tinymce/plugins/myplugin/img/icon.png" style="" data-mce-selected="1">
in editor init function
extended_valid_elements:'img[id|src|class|data-mce-src|data-mce-selected|role|name]',
in otuput HTML no tags id, name
<img src="../tinymce/plugins/myplugin/img/icon.png" class="po" />
When I changed into
extended_valid_elements:'img[id|src|class|data-mce-src|data-mce-selected|role]',
on result still no name
<img id="p_W_1" src="../tinymce/plugins/myplugin/img/icon.png" class="po" />
extended_valid_elements only allows valid HTML5 attributes, and name is not allowed on <img> tags, so name is removed.
Attributes like data-mce-src are internal to tinymce and shouldn't be used, so they are also stripped out.
Please see this example and choose View > Source Code on the editor:
https://fiddle.tiny.cloud/YShaab/5
However, if you add name to extended_valid_elements, you can see that id gets removed too:
https://fiddle.tiny.cloud/YShaab/7
This seems like a bug and I'll report it to the Tiny devs.

custom dojo widget with image display, text title and image description

I am trying build a custom dojo 1.9 widget with template support. I need to pass text value and image (stored in POJO) into the widget when I do Instantiation.
I found some example from:
http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html#quickstart-writingwidgets
there are some templates with text field and button that accept value by using data-dojo-attach-point. But I never find a template example accept image. Does any one have one full example meet my requirement?
<div class="${baseClass}" data-dojo-attach-point="focusNode"
data-dojo-attach-event="ondijitclick:_onClick"
role="menuitem" tabIndex="-1">
<div data-dojo-type="dijit/form/Button"
data-dojo-attach-point="buttonWidget">
My Button
</div>
<span data-dojo-attach-point="containerNode"></span>
</div>
Please help!!
The issue was fixed by myself.
Dojo Dialog and html5 configure tag was used. This way is a work around to support title and image caption, eventually have to wait for dojo LightBoxNano widget (probably 2.0 or newer version) to support image caption.
Dojo snippet code:
var theconfigure = "<figure><img src=\"data:"+returnData.fileType+";base64,"+returnData.fileData+"\" width=\"400\" height=\"300\">"+
"<figcaption>"+returnData.fileDesc+"</figcaption></figure>";
var myDialog = new dijit.Dialog({
title: returnData.relatedLocation,
content: theconfigure,
style: "width: 410px"
});
myDialog.show();
The result looks like:

Images in Magento widgets continued

I've been using Kus's answer found here:
Images in Magento widgets
However moving from my local to the testing server it looks like the class is not being read and I lose the button to upload.
This is my image block
<widgetpress_list type="springwidget_press/List" module="springwidget_press">
....
<image>
<label>Press Image</label>
<description>Image to be displayed on the page</description>
<visible>1</visible>
<type>label</type>
<helper_block>
<type>springwidget_press/imagechooser</type>
<data>
<button translate="open">
<open>Insert Image...</open>
</button>
</data>
</helper_block>
</image>
....
</widgetpress_list>
This is my class name in a file called app/code/local/Spring/Press/Block/ImageChooser.php
class Spring_Press_Block_ImageChooser extends Mage_Adminhtml_Block_Template
{
public function prepareElementHtml(Varien_Data_Form_Element_Abstract $element)
{
.
.
.
}
}
Any ideas?
I would not use a helper block but a custom field type.
Please have a look at my solution on Github. A sample implementation is available here.
An installation package is available on Magento Connect as well.

loading magento cart block to a drop down panel

Can any one please help me to load magento cart block into a drop down box. To be more specific, the module title or a menu should display some thing like 'my basket' and there should be a message of number of items in the cart below 'my basket'. When the user hovers the menu the drop down box should display the content of the cart/sidebar.phtml.Can anyone please suggest me how I can achieve this?
Thanks in advance.
I am doing this for a site I am working on. Unfortunately, I have had to do this through a static block...but it seemed to work without any issues. This is working for me, have a play and see how you go.
The CSS is pretty simple and is they key to showing and hiding the drop down itself.
CSS
// this is the panel where the cart is displayed
DIV#cart-panel {
width:100px; //arbitary width
position:absolute; // need this so that it doesn't interfere with the layout
display:none; // hides the block
z-index:200; // makes it way it in front of other content
}
#cartBtn:hover #cart-panel { display: block; } // basically, when you hover over the cartBtn, the cart-panel displays
Static Block contains
<li id="myCartBtn">
My Cart
{{block type="checkout/cart_sidebar" template="checkout/cart/sidebar.phtml"}}
</li>
Then in my sidebar.phtml, I just make sure there is a div with id="cart-panel" in it, surrounding the cart itself, instead of the <div class="block block-cart">
<?php if ($this->getIsNeedToDisplaySideBar()): ?>
<div id="cart-panel">
...
</div>
<?php endif; ?>
Then, finally, I put the static block into the template where I need it
check Fatdivision AJAX Quick Cart article.
Create an AJAX Quick Cart for Your Magento Theme

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