We sacrifice by not doing any other technology, so that you get the best of Magento.

We sacrifice by not doing any other technology, so that you get the best of Magento.

    Magento 2: Popup minicart after adding product to the cart

    Override the minicart.js on your theme from vendor/magento/module-checkout/view/frontend/web/js/view/minicart.js

    Under the initialize: function () you will see the below code

    $('[data-block="minicart"]').on('contentLoading', function () {
        addToCartCalls++;
        self.isLoading(true);
    });

    Replace with below code

    $('[data-block="minicart"]').on('contentLoading', function () {
        addToCartCalls++;
        self.isLoading(true);
        $('[data-block="minicart"]').on('contentUpdated', function ()  {
            $('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog("open");       
            setTimeout(function() {
                $('[data-block="minicart"]').find('[data-role="dropdownDialog"]').dropdownDialog("close");
                }, 4000);
        });
    });

     

    Magento 2: How to open all product tabs in mobile view by default?

    Follow the below steps to override default.phtml  in your theme.

    Create an default.phtml in : app/design/frontend/Vendor/Themename/Magento_Catalog/templates/product/view

    <?php if ($detailedInfoGroup = $block->getGroupSortedChildNames('detailed_info', 'getChildHtml')):?>
        <div class="product info detailed">
            <?php $layout = $block->getLayout(); ?>        
                <div class="product data items" id="accordion" data-mage-init='{"accordion":{"openedState": "opened", "collapsible": true,  "multipleCollapsible": true}}'>
                <?php foreach ($detailedInfoGroup as $name):?>
                    <?php
                        $html = $layout->renderElement($name);
                        if (!trim($html)) {
                            continue;
                        }
                        $alias = $layout->getElementAlias($name);
                        $label = $block->getChildData($alias, 'title');
                    ?>
                    <div class="data item title"
                         aria-labeledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"
                         data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>">
                        <a class="data switch"
                           tabindex="-1"
                           data-toggle="switch"
                           href="#<?= /* @escapeNotVerified */ $alias ?>"
                           id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title">
                            <?= /* @escapeNotVerified */ $label ?>
                        </a>
                    </div>
                    <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content">
                        <?= /* @escapeNotVerified */ $html ?>
                    </div>
                <?php endforeach;?>
            </div>
        </div>
    <?php endif; ?>
    
    
    <script type="text/javascript">
            require(['jquery', 'matchMedia', 'accordion'], function($, mediaCheck) {
    
                var detailsTabs = $('.product.data.items');
    
                mediaCheck({
                    media: '(min-width: 768px)',
                    // Switch to Desktop Version
                    entry: function () {
                        detailsTabs.accordion({
                            openedState: "opened",
                            collapsible: false                        
                        });
                    },
                    // Switch to Mobile Version
                    exit: function () {
                        detailsTabs.accordion({
                            openedState: "opened",
                            collapsible: true,
                            active: [0,1,2,3,4],
                            multipleCollapsible: true
    
                        });
                    }
                });
            })
    </script>

     

     

    How to show FromDate and ToDate on detail page from assigned catalog price rule?

    The default Magento 2 does not show FromDate or ToDate on the detail page from the assigned catalog price rule. If you want to show these dates on the detail page then follow the below steps.

    Step 1: Create a registration file like Magemonkeys/Catalogrule/registration.php

    <?php
    MagentoFrameworkComponentComponentRegistrar::register(
    MagentoFrameworkComponentComponentRegistrar::MODULE,
    'Magemonkeys_Catalogrule',
    __DIR__
    );

     

    Step 2: Create module file like Magemonkeys/Catalogrule/etc/module.xml

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Magemonkeys_Catalogrule" setup_version="1.0.0"/>
    </config>

     

    Step 3: Create block file like Magemonkeys/Catalogrule/Block/Rule.php

    <?php
        namespace MagemonkeysCatalogruleBlock;
        use MagentoFrameworkViewElementTemplate;
        use MagentoFrameworkRegistry;
        use MagentoCatalogModelProduct;
        use MagentoFrameworkExceptionLocalizedException;
    
        class Rule extends MagentoFrameworkViewElementTemplate
        {
            protected $_registry;
    
            public function __construct(
                MagentoBackendBlockTemplateContext $context,
                MagentoCatalogRuleModelResourceModelRuleCollectionFactory $ruleFactory,
                MagentoFrameworkRegistry $registry,
                array $data = []
            ) {
                $this->_registry = $registry;
                $this->_ruleFactory = $ruleFactory;
                parent::__construct($context, $data);
        }
    
        public function getCatalogRuleId()
        {
            //die('aaaaa');
            $catalogRuleCollection = $this->_ruleFactory->create()
                ->addFieldToFilter('is_active',1);
    
            return $catalogRuleCollection;
    
            $resultProductIds = [];
    
            foreach ($catalogRuleCollection as $catalogRule) {
                $productIdsAccToRule = $catalogRule->getMatchingProductIds();
    
                //echo json_encode($productIdsAccToRule); exit;
    
                $websiteId = $this->_storeManager->getStore()->getWebsiteId();
    
                foreach ($productIdsAccToRule as $productId => $ruleProductArray) {
                    if (!empty($ruleProductArray[$websiteId])) {
                        $resultProductIds[$productId] = $catalogRule->getData();
                    }
                }
    
                return $resultProductIds;
            }
        }
    
        public function getCurrentProduct()
        {        
            return $this->_registry->registry('current_product');
        } 
    
        public function getCatalogPriceRuleProductIds()
        {
            $storeManager = MagentoFrameworkAppObjectManager::getInstance()->create(
                 'MagentoStoreModelStoreManagerInterface'
            );
            $catalogRule = MagentoFrameworkAppObjectManager::getInstance()->create(
                 'MagentoCatalogRuleModelRuleFactory'
            );
            
            $websiteId = $storeManager->getStore()->getWebsiteId();//current Website Id
    
            $resultProductIds = [];
            $catalogRuleCollection = $catalogRule->create()->getCollection();
            $catalogRuleCollection->addIsActiveFilter(1);//filter for active rules only
            foreach ($catalogRuleCollection as $catalogRule) {
                $productIdsAccToRule = $catalogRule->getMatchingProductIds();
                foreach ($productIdsAccToRule as $productId => $ruleProductArray) {
                    if (!empty($ruleProductArray[$websiteId])) {
                        $resultProductIds[$productId]['rulename'] = $catalogRule->getName();
                        $resultProductIds[$productId]['fromdate'] = $catalogRule->getFromDate();
                        $resultProductIds[$productId]['todate'] = $catalogRule->getToDate();
                    }
                }
            }
            return $resultProductIds;
        }
    }

     

    Step 4: Create layout file like Magemonkeys/Catalogrule/view/frontend/layout/catalog_product_view.xml

    <?xml version="1.0"?>
    <!--
    /**
     * Copyright © 2018 Porto. All rights reserved.
     * See COPYING.txt for license details.
     */
    -->
    <page layout="2columns-right" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
             <referenceContainer name="product.info.main">
                <block class="MagemonkeysCatalogruleBlockRule" name="rule_dates" template="Magemonkeys_Catalogrule::product/view/rule.phtml"/>
            </referenceContainer>
            <move element="rule_dates" destination="product.info.main" before="product.info.price"/>
        </body>
    </page>

     

    Step 5 : Create template file like Magemonkeys/Catalogrule/view/frontend/templates/product/view/rule.phtml

    <?php
        $result=$block->getCatalogRuleId();
        $product=$block->getCurrentProduct();
        $collection=$block->getCatalogPriceRuleProductIds();
        $fromdate = '';
        $todate = '';
        $flag = false;
        foreach ($collection as $key => $collectionnew) {
            if ($product->getId() == $key) {
                $fromdate = $collectionnew['fromdate'];
                $todate = $collectionnew['todate'];
                $flag = true;
                break;
            }
        }
    ?>
    <?php if($flag){ ?>
        <div class="promotions-date">
            <h3 class="promotions"><?php echo __("Promotion"); ?></h3>
            <span class="fromdate"><?php echo __("Start Date : "); ?><?php echo $fromdate; ?></span>
            <span class="todate"><?php echo __("End Date : "); ?><?php echo $todate; ?></span>
        </div>
    <?php } ?>
    <style type="text/css">
        .promotions-date { margin-bottom: 20px; }
        .promotions-date .promotions { margin-top: 0; color: #333; }
        .promotions-date span { display: block; }
    </style>

     

    Step 6: Then run the below commands.

    php bin/magento setup:upgrade
    php bin/magento setup:static-content:deploy
    php bin/magento cache:flush

     

    That’s it.

    Now clean cache and check your product detail page. Now your catalog price rule FromDate and ToDate should be visible.

    Magento 2: How to resolve category save error ‘Something went wrong while saving the category’?

    Sometimes Magento 2.3.6 upgrade after fetching this issue, then run below SQL query inside the direct database to get the solution.

    INSERT INTO `eav_attribute` (`entity_type_id`, `attribute_code`, `attribute_model`, `backend_model`, `backend_type`, `backend_table`, `frontend_model`, `frontend_input`, `frontend_label`, `frontend_class`, `source_model`, `is_required`, `is_user_defined`, `default_value`, `is_unique`, `note`) VALUES
    
    (4, 'custom_layout_update_file', NULL, 'Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate', 'varchar', NULL, NULL, 'select', 'Custom Layout Update', NULL, 'Magento\Catalog\Model\Product\Attribute\Source\LayoutUpdate', 0, 0, NULL, 0, NULL),
    (3, 'custom_layout_update_file', NULL, 'Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate', 'varchar', NULL, NULL, 'select', 'Custom Layout Update', NULL, 'Magento\Catalog\Model\Category\Attribute\Source\LayoutUpdate', 0, 0, NULL, 0, NULL);

     

    How to add external CSS and JS in Magento 2?

    Today we are going to discuss that how to add third-party js and CSS in our Magento 2 via layout XML.

    We have to add CSS and JS in layout via layout.

    The syntax for CSS in layout

    <head>
    <css src="https://cloudflare.com/assets/owl.carousel.css" src_type="url" />
    </head>

    The syntax for JS in layout

    <head>
    <script src="https://cloudflare.com/assets/owl.carousel.js" src_type="url" />
    </head>

    as per above, we can add this in our layout file

    That’s it.

    How to apply cart rule in cart in magento 2

    Here we can get cart rule from quote, so we can apply cart rule based on quote

    This is making module : Magemonkeys/Generalcart

    This is block file

    <?php
    namespace MagemonkeysGeneralcartBlock;
    
    class Simplequote extends MagentoFrameworkViewElementTemplate
    {
        protected $sessionmodelFactory;
    	protected $quotemodelFactory;
    
        public function __construct(
            MagentoFrameworkViewElementTemplateContext $context,
            MagentoCheckoutModelSessionFactory $sessionmodelFactory,
            MagentoQuoteModelQuoteFactory $quotemodelFactory,
            array $data = []
        ) {
            $this->sessionFactory = $sessionmodelFactory;
            $this->quoteFactory = $quotemodelFactory;
            parent::__construct($context, $data);
        }
    
        public function getAppliedIdsforrule()
        {
            $quoteId = $this->sessionFactory->create()->getQuote()->getId();
            $quote = $this->quoteFactory->create()->loadActive($quoteId);
            $salesruleIds = explode(',', $quote->getAppliedRuleIds());
            return $salesruleIds;
        }
    }

    we can call this getAppliedIdsforrule() function in block
    so its return rule ids.

    Magento 2 Add Custom Discount Separate Row in Summary

    1) Create a sales.xml file on app/code/Vendor/module/etc

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
        <section name="quote">
            <group name="totals">
                <item name="promodiscount" instance="VendorModuleModelQuoteDiscount" sort_order="500"/>
            </group>
        </section>
    </config>

    2) Create file Discount.php on this path appcodeVendorModuleModelQuoteDiscount

    namespace VendorModuleModelQuote;
    
    class Discount extends MagentoQuoteModelQuoteAddressTotalAbstractTotal
    {
    
        public function collect(
            MagentoQuoteModelQuote $quote,
            MagentoQuoteApiDataShippingAssignmentInterface $shippingAssignment,
            MagentoQuoteModelQuoteAddressTotal $total
        ) {
            parent::collect($quote, $shippingAssignment, $total);
    
                $customDiscount = -20;
    
                $total->addTotalAmount('customdiscount', $customDiscount);
                $total->addBaseTotalAmount('customdiscount', $customDiscount);
                $quote->setCustomDiscount($customDiscount);
            }
            return $this;
        }
    
        /**
         * Assign subtotal amount and label to address object
         *
         * @param MagentoQuoteModelQuote $quote
         * @param AddressTotal $total
         * @return array
         * @SuppressWarnings(PHPMD.UnusedFormalParameter)
         */
        public function fetch(MagentoQuoteModelQuote $quote, MagentoQuoteModelQuoteAddressTotal $total)
        {	
        	$customDiscount = 20;
            return [
                'code' => 'Custom_Discount',
                'title' => $this->getLabel(),
                'value' => $customDiscount
            ];
        }
    
        /**
         * get label
         * @return string
         */
        public function getLabel()
        {
            return __('Custom Discount');
        }
    }

    3) Create file checkout_cart_index.xml on path appcodeVendorModuleviewfrontendlayout

    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
    	<referenceBlock name="checkout.cart.totals">
    	   <arguments>
    	       <argument name="jsLayout" xsi:type="array">
    	           <item name="components" xsi:type="array">
    	               <item name="block-totals" xsi:type="array">
    	                   <item name="children" xsi:type="array">
    	                       <item name="customdiscount" xsi:type="array">
    	                           <item name="component"  xsi:type="string">Vendor_Module/js/view/checkout/summary/custom-discount</item>
    	                           <item name="sortOrder" xsi:type="string">30</item>
    	                           <item name="config" xsi:type="array">
    	                               <item name="customdiscount" xsi:type="string" translate="true">Custom Discount</item>
    	                           </item>
    	                       </item>
    	                   </item>
    	               </item>
    	           </item>
    	       </argument>
    	   </arguments>
    	</referenceBlock>
        </body>
    </page>

    4) Create file checkout_index_index.xml on path appcodeVendorModuleviewfrontendlayout

    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceBlock name="checkout.root">
                <arguments>
                    <argument name="jsLayout" xsi:type="array">
                        <item name="components" xsi:type="array">
                            <item name="checkout" xsi:type="array">
                                <item name="children" xsi:type="array">
                                    <item name="sidebar" xsi:type="array">
                                        <item name="children" xsi:type="array">
                                            <item name="summary" xsi:type="array">
                                                <item name="children" xsi:type="array">
                                                    <item name="totals" xsi:type="array">
                                                        <item name="children" xsi:type="array">
                                                           <item name="customdiscount" xsi:type="array">
                                                                <item name="component"  xsi:type="string">Vendor_Module/js/view/checkout/cart/totals/custom-discount</item>
                                                                <item name="sortOrder" xsi:type="string">20</item>
                                                                <item name="config" xsi:type="array">
                                                                     <item name="template" xsi:type="string">Vendor_Module/checkout/cart/totals/custom-discount</item>
                                                                    <item name="title" xsi:type="string" translate="true">Custom Discount</item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                    <item name="cart_items" xsi:type="array">
                                                        <item name="children" xsi:type="array">
                                                            <item name="details" xsi:type="array">
                                                                <item name="children" xsi:type="array">
                                                                    <item name="subtotal" xsi:type="array">
                                                                        <item name="component" xsi:type="string">Magento_Tax/js/view/checkout/summary/item/details/subtotal</item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </argument>
                </arguments>
            </referenceBlock>
        </body>
    </page>

    5) Create file custom-discount.js on path app/code/Vendor/Module/view/frontend/web/js/view/checkout/cart/totals

    define(
        [
            'Vendor_Module/js/view/checkout/summary/custom-discount'
        ],
        function (Component) {
            'use strict';
    
            return Component.extend({
    
                /**
                 * @override
                 */
                isDisplayed: function () {
                    return true;
                }
            });
        }
    );

    6) Create file custom-discount.html on path app/code/Vendor/Module/view/frontend/web/template/checkout/cart/totals

    <!-- ko if: isDisplayedCustomdiscountTotal() -->
    <tr class="totals customdiscount excl">
        <th class="mark" colspan="1" scope="row" data-bind="text: title"></th>
        <td class="amount">
            <span class="price" data-bind="text: getCustomdiscountTotal()"></span>
        </td>
    </tr>
    <!-- /ko -->

    7) Create file on this path custom-discount.js app/code/Vendor/Module/view/frontend/web/js/view/checkout/summary

    define(
        [
           'jquery',
           'Magento_Checkout/js/view/summary/abstract-total',
           'Magento_Checkout/js/model/quote',
           'Magento_Checkout/js/model/totals',
           'Magento_Catalog/js/price-utils'
        ],
        function ($,Component,quote,totals,priceUtils) {
            "use strict";
            return Component.extend({
                defaults: {
                    template: 'Vendor_Module/checkout/summary/custom-discount'
                },
                totals: quote.getTotals(),
                isDisplayedCustomdiscountTotal : function () {
                    return true;
                },
                getCustomdiscountTotal : function () {
                    var price = totals.getSegment('Custom_Discount').value;
                    return this.getFormattedPrice(price);
                }
             });
        }
    );

    8) Create file custom-discount.html on path app/code/Vendor/Module/view/frontend/web/template/checkout/summary

    <!-- ko if: isDisplayedCustomdiscountTotal() -->
    <tr class="totals coupon-discount excl">
       <th class="mark" colspan="1" scope="row" data-bind="text: customdiscount"></th>
       <td class="amount">
           <span class="price" data-bind="text: getCustomdiscountTotal(), attr: {'data-th': customdiscount}"></span>
       </td>
    </tr>
    <!-- /ko -->

     

    Fix Magento 2.3.5 Content Security Warnings

    I got a solution by removing all the content security warnings by creating a module and adding the csp_whitelist.xml in the etc folder of the module.

    Step 1: Create a module.

    Step 2: Add csp_whitelist.xml in the etc folder of the module and copy-paste the below code in that file.

    <?xml version="1.0"?>
    <csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd">
        <policies>
            <policy id="script-src">
                <values>
                    <!--CDN-->
                    <value id="cloudflare" type="host">*.cloudflare.com</value>
                    <!--Google-->
                    <value id="google-analytics" type="host">*.google-analytics.com</value>
                    <value id="googlecom" type="host">*.google.com</value>
                    <value id="googlein" type="host">*.google.co.in</value>
                    <value id="gtmanager" type="host">*.googletagmanager.com</value>
                    <value id="gstatic" type="host">*.gstatic.com</value>
                    <!--Hotjar-->
                    <value id="hotjar" type="host">*.hotjar.com</value>
                    <!--Criteo-->
                    <value id="criteo" type="host">*.criteo.com</value>
                    <value id="criteonet" type="host">*.criteo.net</value>
                    <!--Github-->
                    <value id="github" type="host">*.github.io</value>
                </values>
            </policy>
            <policy id="style-src">
                <values>
                    <!--CDN-->
                    <value id="cloudflare" type="host">*.cloudflare.com</value>
                    <!--Design-->
                    <value id="googlefont" type="host">fonts.googleapis.com</value>
                    <value id="maxcdn" type="host">*.bootstrapcdn.com</value>
                </values>
            </policy>
            <policy id="img-src">
                <values>
                    <!--CDN-->
                    <value id="cloudflare" type="host">*.cloudflare.com</value>
                    <value id="klarna-base" type="host">https://cdn.klarna.com</value>
                    <!--Payments-->
                    <value id="paypal" type="host">*.paypal.com</value>
                    <!--Video-->
                    <value id="vimeocdn" type="host">*.vimeocdn.com</value>
                    <value id="youtube-img" type="host">https://s.ytimg.com</value>
                    <!--Google-->
                    <value id="googlecom" type="host">*.google.com</value>
                    <value id="googlein" type="host">*.google.co.in</value>
                    <!--Data-->
                    <value id="data" type="host">data:</value>
                </values>
            </policy>
            <policy id="connect-src">
                <values>
                    <!--Google-->
                    <value id="google-analytics" type="host">*.google-analytics.com</value>
                    <value id="gtmanager" type="host">*.googletagmanager.com</value>
                    <!--CDN-->
                    <value id="cloudflare" type="host">*.cloudflare.com</value>
                    <!--Payments-->
                    <value id="paypal" type="host">*.paypal.com</value>
                    <!--Double Click-->
                    <value id="doubleclick" type="host">*.doubleclick.net</value>
                </values>
            </policy>
            <policy id="frame-src">
                <values>
                    <!--Criteo-->
                    <value id="criteo" type="host">*.criteo.com</value>
                    <value id="criteonet" type="host">*.criteo.net</value>
                    <!--Hotjar-->
                    <value id="hotjar" type="host">*.hotjar.com</value>
                    <!--Google-->
                    <value id="googlecom" type="host">*.google.com</value>
                    <value id="googlein" type="host">*.google.co.in</value>
                    <!--Github-->
                    <value id="github" type="host">*.github.io</value>
                </values>
            </policy>
            <policy id="font-src">
                <values>
                    <!--CDN-->
                    <value id="cloudflare" type="host">*.cloudflare.com</value>
                    <!--Design-->
                    <value id="googlefont" type="host">fonts.googleapis.com</value>
                    <value id="maxcdn" type="host">*.bootstrapcdn.com</value>
                </values>
            </policy>
        </policies>
    </csp_whitelist>

    Clean the cache and check the site again. Probably all the Content security warnings will be removed by adding the above file in your module.

    I think It will cover most of the domain, but if you face any other content security warnings then you can add the domain in the csp_whitelist.xml file.

    Hope this article will help you to fix Magento 2.3.5 content security warnings.

    Magento2 Form Validation Without Form Submit

    require([
        'jquery',
        'mage/validation'
    ], function($){
    
        var dataForm = $('#form-validate');
        var ignore = null;
    
        dataForm.mage('validation', {
            ignore: ignore ? ':hidden:not(' + ignore + ')' : ':hidden'
        }).find('input:text').attr('autocomplete', 'off');
    
        $('button#my-button').click( function() { //can be replaced with any event
            dataForm.validation('isValid'); //validates form and returns boolean
        });
    });

    #form-validate – Replace with your form id

    #my-button – Replace with your button id