<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>gj |</title>
	<atom:link href="https://blog.gaiterjones.com/category/magento/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.gaiterjones.com/category/magento/</link>
	<description>gaiterjones</description>
	<lastBuildDate>Thu, 30 Jan 2020 17:49:31 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.4.3</generator>
	<item>
		<title>Exporting Magento 1 Product Reviews and Importing them to Magento 2</title>
		<link>https://blog.gaiterjones.com/exporting-magento-1-product-reviews-and-importing-them-to-magento-2/</link>
					<comments>https://blog.gaiterjones.com/exporting-magento-1-product-reviews-and-importing-them-to-magento-2/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Sat, 11 Jan 2020 17:38:43 +0000</pubDate>
				<category><![CDATA[Mage Migration]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento Product Export]]></category>
		<category><![CDATA[Magento2]]></category>
		<category><![CDATA[Product Review Import]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=2123</guid>

					<description><![CDATA[I am Migrating Magento 1.9 to 2.3 &#8211; yikes! I wasn&#8217;t able to use the Magento 2 migration tool for products so one of the tasks on my TO DO...<a class="more-link" href="https://blog.gaiterjones.com/exporting-magento-1-product-reviews-and-importing-them-to-magento-2/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>I am Migrating Magento 1.9 to 2.3 &#8211; yikes!</p>
<p>I wasn&#8217;t able to use the Magento 2 migration tool for products so one of the tasks on my TO DO list was product review migration. Looking around for (free) solutions the only stuff I came across was no longer free or didn&#8217;t work. $100 to import 500 reviews &#8211; I don&#8217;t think so.</p>
<p>If you are looking to do something similar here is a quick copy and paste of the php code I used for the Magento 1 export, and the Magento 2 import.</p>
<p>I am assuming you can plug this into your existing Magento php cli tools which already bootstrap the corresponding Magento1 or 2 core classes. For Magento 1 I first took a full product collection and parsed each product for review data. All the data is saved to a tab seperated CSV file which is then used for the Magento 2 product review import.</p>
<figure style="width: 800px" class="wp-caption aligncenter"><img fetchpriority="high" decoding="async" src="https://blog.gaiterjones.com/dropbox/mage-product-review-migration.gif" width="800" height="600" alt="Magento Migration Product Review Migration" class="size-large" /><figcaption class="wp-caption-text">Magento Migration Product Review Migration</figcaption></figure>
<h2>Magento 1.9 product review export to csv</h2>
<pre class="brush: php; title: ; notranslate">
echo $c(&quot;MAGE1 Migration - Export MAGE1 Product Reviews to CSV&quot;)-&gt;header. &quot;\n\n&quot;;

// LOAD ALL PRODUCTS
//
$_storeID=0; // admin

$_reviewVisibleInStoreViews=array(1,2);

$_storeLocale&#x5B;0]='admin';
$_storeLocale&#x5B;1]='de';
$_storeLocale&#x5B;2]='en';
$this-&gt;getProductCollectionAllSKUs($_storeID);

$_products=$this-&gt;get('collection');
$_reviewData=array();
$_count=0;

foreach($_products as $_id=&gt;$_product)
{
    $_sku = $_product-&gt;getSku();
    $_productId = $_product-&gt;getId();
    $_reviewcCollection = \Mage::getModel('review/review')-&gt;getCollection()
        -&gt;addStoreFilter($_storeID)
        -&gt;addEntityFilter('product', $_productId)
        -&gt;addStatusFilter(\Mage_Review_Model_Review::STATUS_APPROVED)
        -&gt;setDateOrder()
        -&gt;addRateVotes();

        if (count($_reviewcCollection))
        {

            $_productReview=array();

            foreach($_reviewcCollection as $_review) {

                $_ratings=array();
                $votes = $_review-&gt;getRatingVotes();
                $total = 0;

                foreach($votes as $vote)
                {
                    $total += $vote-&gt;getPercent();
                    $_ratings&#x5B;]=$vote-&gt;getValue();
                }

                $avgRating = $total / count($votes);

                $_reviewDetail=trim(preg_replace('/\s\s+/', '&lt;br&gt;', $_review&#x5B;'detail']));
                $_reviewTitle=trim(preg_replace('/\s\s+/', '&lt;br&gt;', $_review&#x5B;'title']));

                //var_dump($total, count($votes), $avg);
                $_productReview&#x5B;]=array(
                    'createdat' =&gt; $_review-&gt;getCreatedAt(),
                    'review' =&gt; $_reviewDetail,
                    'summary' =&gt; $_reviewTitle,
                    'nickname' =&gt; $_review&#x5B;'nickname'],
                    'customer_id' =&gt; ($_review&#x5B;'customer_id'] ? $_review&#x5B;'customer_id'] : null),
                    'ratings' =&gt; implode('|', $_ratings),
                    'visibilty' =&gt; implode('|', $_reviewVisibleInStoreViews),
                );

                $_count++;
                echo '.';

            }

            $_reviewData&#x5B;$_sku]=$_productReview;

        }

}

//print_r($_reviewData);
echo &quot;\n&quot;.$c($_count. ' product reviews exported for store view '. $_storeID)-&gt;info.&quot;\n&quot;;

// EXPORT CSV
//
//
$_now = new \DateTime(null, new \DateTimeZone('Europe/Berlin'));
$_reviewExportFilename='/home/data/Mage1MigrateReviewsProducts_'.$_now-&gt;format('Y-m-d').'_'.$_storeLocale&#x5B;$_storeID].'.csv';

$_reviewExportHeader='sku,createdat,review,summary,nickname,customer_id,ratings,visibility';
$_reviewExportHeader=explode(',',$_reviewExportHeader);

if (file_exists($_reviewExportFilename)) {unlink($_reviewExportFilename);}

// -- csv seperator
$_seperator=&quot;\t&quot;; // TAB
//$_seperator=','; // COMMA

ini_set('default_charset','UTF-8');

$_fileHandle = fopen($_reviewExportFilename, 'w');

    // write UTF8-BOM
    //fwrite($_fileHandle, pack(&quot;CCC&quot;,0xef,0xbb,0xbf));

    // write header
    if ($_reviewExportHeader) {fwrite($_fileHandle, implode($_seperator, $_reviewExportHeader).&quot;\r\n&quot;);}

    echo $c('Creating CSV export file '. $_reviewExportFilename)-&gt;info.&quot;\n&quot;;

    // write data
    foreach ($_reviewData as $_sku =&gt; $_reviews)
    {
        foreach ($_reviews as $_review)
        {
            fwrite($_fileHandle, $_sku.$_seperator.implode($_seperator, $_review).&quot;\r\n&quot;);
        }
    }

fclose($_fileHandle);

echo $c('Product review export complete!')-&gt;success. &quot;\n&quot;;

</pre>
<h2>Magento 2 csv product import</h2>
<pre class="brush: php; title: ; notranslate">
&lt;?php

	echo $c(&quot;MAGE2 Import Product Reviews from MAGE1 Migration CSV&quot;)-&gt;header. &quot;\n\n&quot;;

	$_reviewDataCSVFile='/home/data/Mage1MigrateReviewsProducts_2020-01-11_admin.csv';
	$_reviewDataCSV=file('/home/data/Mage1MigrateReviewsProducts_2020-01-11_admin.csv', FILE_IGNORE_NEW_LINES);

	echo $c(&quot;Importing from &quot;. $_reviewDataCSVFile)-&gt;info. &quot;\n&quot;;

	$_count=0;
	$_total=(count($_reviewDataCSV)-1);
	foreach ($_reviewDataCSV as $_key =&gt; $_data)
	{
		if ($_key===0){ // build header
			$_keys=explode(&quot;\t&quot;,trim($_data));
			continue;
		}

		$_dataArray=explode(&quot;\t&quot;,$_data);

		foreach ($_dataArray as $_key =&gt; $_data)
		{
			$_reviewData&#x5B;$_keys&#x5B;$_key]]=$_data;
		}

		//print_r($_reviewData);

		try
		{
			$_success=false;

            $_obj=new \PAJ\Library\Magento2\Reviews\AddReview(
                array(
					'reviewdata' =&gt; $_reviewData,
					'storeid' =&gt; '0'
                )
            );
				$_output=$_obj-&gt;get('output');
				$_success=$_obj-&gt;get('success');

			if($_success)
			{
				$_count++;
				echo $c(&quot;Review &quot;. $_count.&quot;/&quot;.$_total. &quot; imported.&quot;)-&gt;success. &quot;\n&quot;;
				//print_r($_output);

			}

		}
		catch (\Exception $e)
		{
			// catch bad guys
			//throw new \Exception($e);
			echo $c(&quot;ERROR: &quot;. $e-&gt;getMessage())-&gt;error. &quot;\n&quot;;
			continue;
		}

		// 1 product test exit;
		//exit;
	}


if (!$_silent) {

	echo 'Import complete.'. &quot;\n&quot;;
}


exit;


class MagentoAddReview extends AbstractApp
{

    /**
     * @var \Magento\Review\Model\RatingFactory;
     */
    private $_ratingFactory;
    /**
     * @var \Magento\Review\Model\ReviewFactory;
     */
    private $_reviewFactory;

    public function run()
    {
        // load a product
        //

        // init vars
        $_data=array();

        $_storeID=$this-&gt;_variables&#x5B;'storeid'];
        $_store = $this-&gt;_storeManagerInterface-&gt;getStore($_storeID);
        $this-&gt;_storeManagerInterface-&gt;setCurrentStore($_store);
        $_reviewData=$this-&gt;_variables&#x5B;'reviewdata'];

        $_sku=$_reviewData&#x5B;'sku'];
        $_customerId=$_reviewData&#x5B;'customer_id'];

        //for Guest user $_customerId=Null;
        //
        if (!$_customerId){$_customerId=Null;}

        // create review data
        //
        $_customerNickName=$_reviewData&#x5B;'nickname'];
        $_reviewTitle=$_reviewData&#x5B;'summary'];
        $_reviewDetail=preg_replace('#&lt;br\s*/?&gt;#i', &quot;\n&quot;, $_reviewData&#x5B;'review']);
        $_title=preg_replace('#&lt;br\s*/?&gt;#i', &quot;\n&quot;, $_reviewData&#x5B;'summary']);
        $_createdAt=$_reviewData&#x5B;'createdat'];

        // store visibility array, i.e. 1,2 for visibile in store view 1 and 2
        // 
        $_reviewVisibleInStoreViews=explode('|',$_reviewData&#x5B;'visibility']);
        $_ratings=explode('|',$_reviewData&#x5B;'ratings']);

        $_product = $this-&gt;_productRepository-&gt;get($_sku);
        $_productId=$_product-&gt;getId();

        $_reviewFactory = $this-&gt;getReviewFactory();
        $_ratingFactory = $this-&gt;getRatingFactory();

        foreach ($_ratings as $_key =&gt; $_rating)
        {
            $_reviewFinalData&#x5B;'ratings']&#x5B;$_key+1] = $_rating;
        }

        $_reviewFinalData&#x5B;'nickname'] = $_customerNickName; //add user nickname
        $_reviewFinalData&#x5B;'title'] = $_title; //add title of the review
        $_reviewFinalData&#x5B;'detail'] = $_reviewDetail; //add details of the review
        $_reviewFinalData&#x5B;'customerid'] = $_customerId;

        $review = $_reviewFactory-&gt;create()-&gt;setData($_reviewFinalData);

        $review-&gt;unsetData('review_id');
        $review-&gt;setEntityId($review-&gt;getEntityIdByCode(\Magento\Review\Model\Review::ENTITY_PRODUCT_CODE))
            -&gt;setEntityPkValue($_productId)
            -&gt;setStatusId(\Magento\Review\Model\Review::STATUS_APPROVED) //By default set approved
            -&gt;setStoreId($_storeID)
            -&gt;setCreatedAt($_createdAt)
            -&gt;setStores($_reviewVisibleInStoreViews)
            -&gt;save();

        foreach ($_reviewFinalData&#x5B;'ratings'] as $ratingId =&gt; $rating) {
            $_ratingFactory-&gt;create()
                -&gt;setRatingId($ratingId)
                -&gt;setReviewId($review-&gt;getId())
                -&gt;addOptionVote($rating, $_productId);
        }
        $review-&gt;aggregate();

        //$this-&gt;debug($_data);
        $_data&#x5B;'store']&#x5B;'storeid']=$_storeID;
        $_data&#x5B;'reviews']=array(
            'productid'=&gt; $_productId
        );


        return &#x5B;'magento' =&gt; $_data];
    }

    /**
     * Get Factories
     *
     */
    private function getRatingFactory()
    {
        if (!$this-&gt;_ratingFactory) {
            $this-&gt;_ratingFactory = \Magento\Framework\App\ObjectManager::getInstance()
                -&gt;get(\Magento\Review\Model\RatingFactory::class);
        }
        return $this-&gt;_ratingFactory;
    }
    private function getReviewFactory()
    {
        if (!$this-&gt;_reviewFactory) {
            $this-&gt;_reviewFactory = \Magento\Framework\App\ObjectManager::getInstance()
                -&gt;get(\Magento\Review\Model\ReviewFactory::class);
        }
        return $this-&gt;_reviewFactory;
    }
}

</pre>
<p>The echo&#8217;s use the cool colours from <a href="https://github.com/kevinlebrun/colors.php">https://github.com/kevinlebrun/colors.php</a> The will probably throw an error for you, sorry!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/exporting-magento-1-product-reviews-and-importing-them-to-magento-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Create Custom Product Attributes and Groups in Magento 2</title>
		<link>https://blog.gaiterjones.com/create-custom-product-attributes-and-groups-in-magento-2/</link>
					<comments>https://blog.gaiterjones.com/create-custom-product-attributes-and-groups-in-magento-2/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Fri, 19 Jul 2019 10:14:08 +0000</pubDate>
				<category><![CDATA[Catalog]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento2]]></category>
		<category><![CDATA[Magento2 Migration]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=2075</guid>

					<description><![CDATA[Whilst migrating from Magento 1 to Magento 2 you might want to quickly recreate product attributes in Magento 2 programatically. Adding a lot of product attributes manually can be very...<a class="more-link" href="https://blog.gaiterjones.com/create-custom-product-attributes-and-groups-in-magento-2/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>Whilst migrating from Magento 1 to Magento 2 you might want to quickly recreate product attributes in Magento 2 programatically. Adding a lot of product attributes manually can be very time consuming so using the setup script in one of your modules let&#8217;s you quickly create new attributes.</p>
<p>An example of the install method is shown below with code for a confiurable product dropdown attribute and a text attribute.</p>
<p>To run the install script use <em>php bin/magento setup:upgrade</em></p>
<p>To force the install script to run again you can delete the module entry in the setup_module database table and run <em>setup:upgrade</em> again. I guess a cleverer way would be to use the upgrade feature but this is really just intended as a one off process to perhaps install 50 product attributes that would otherwise be time consuming to manually configure.</p>
<p><img decoding="async" src="https://blog.gaiterjones.com/wp-content/uploads/2019/07/gaiterjones_catalog_module_setup.jpg" alt="" width="564" height="300" class="aligncenter size-full wp-image-2088" /></p>
<p>Note the group name let&#8217;s you add these custom attributes into their own group which makes them more manageable in the admin interface.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php namespace GaiterJones\Catalog\Setup; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; use Magento\Framework\Setup\InstallDataInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; class InstallData implements InstallDataInterface { private $eavSetupFactory; public function __construct(EavSetupFactory $eavSetupFactory) { $this-&gt;eavSetupFactory = $eavSetupFactory;
    }

    // CUSTOM product attributes / groups
    //
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $eavSetup = $this-&gt;eavSetupFactory-&gt;create(&#x5B;'setup' =&gt; $setup]);

        // attribute for configurable product
        //
        //
        $eavSetup-&gt;addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'product_colour_x',
            &#x5B;
                'type' =&gt; 'int',
                'group' =&gt; 'GAITERJONES Product',
                'backend' =&gt; '',
                'frontend' =&gt; '',
                'label' =&gt; 'product_colour_x',
                'default' =&gt; '',
                'input' =&gt; 'select',
                'class' =&gt; '',
                'source' =&gt; 'Magento\Eav\Model\Entity\Attribute\Source\Table',
                'global' =&gt; \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible' =&gt; true,
                'required' =&gt; false,
                'user_defined' =&gt; true,
                'searchable' =&gt; false,
                'filterable' =&gt; true,
                'comparable' =&gt; false,
                'visible_on_front' =&gt; false,
                'used_in_product_listing' =&gt; false,
                'unique' =&gt; false,
                'apply_to' =&gt; ''
            ]
        );

        $eavSetup-&gt;addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'selling_unit',
            &#x5B;
                'type' =&gt; 'varchar',
                'group' =&gt; 'GAITERJONES Product',
                'backend' =&gt; '',
                'frontend' =&gt; '',
                'label' =&gt; 'Selling Unit',
                'input' =&gt; 'text',
                'class' =&gt; '',
                'source' =&gt; '',
                'global' =&gt; \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible' =&gt; true,
                'required' =&gt; false,
                'user_defined' =&gt; true,
                'default' =&gt; 'Stück',
                'searchable' =&gt; false,
                'filterable' =&gt; false,
                'comparable' =&gt; false,
                'visible_on_front' =&gt; false,
                'used_in_product_listing' =&gt; false,
                'unique' =&gt; false,
                'apply_to' =&gt; ''
            ]
        );

     }
}

</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/create-custom-product-attributes-and-groups-in-magento-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magento Migration for Dummies</title>
		<link>https://blog.gaiterjones.com/magento-migration-for-dummies/</link>
					<comments>https://blog.gaiterjones.com/magento-migration-for-dummies/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Tue, 18 Jun 2019 14:45:21 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento2 Migration]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=2059</guid>

					<description><![CDATA[If you are a Magento 1 Ecommerce Store owner you are probably well aware that support for Magento CE 1.x will stop in 2020. The nice folks at Magento call...<a class="more-link" href="https://blog.gaiterjones.com/magento-migration-for-dummies/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[
<p>If you are a Magento 1 Ecommerce Store owner you are probably well aware that support for Magento CE 1.x will stop in 2020.</p>



<p>The nice folks at Magento call it &#8220;<em>Sunsetting</em>&#8220;</p>



<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" width="862" height="318" src="https://blog.gaiterjones.com/wp-content/uploads/2019/05/mage1-sunset.jpg" alt="" class="wp-image-2060" /></figure>
</div>



<p>We have all known this for some time, exactly what that means for Magento 1 stores is open for debate, I doubt the world is going to end but are the risks associated with running deprecated ecommerce store software acceptable?</p>



<p>Magento 2 has been maturing nicely over the last few years and now is definitely as good a time as any to start working on your migration plan.</p>



<p>The costs and time associated with migrating a Magento1 store with &#8220;minimal&#8221; third party integration are estimated at between $5,000 to $60,000 &#8211; 3 to 6 months. Even a very basic migration will require a new custom theme, modules and data export.</p>



<p>The total cost of migration depends of course on the in house skills you or your company possess. Chances are if you are reading this you already have Magento 1 and PHP experience. That&#8217;s good news! If you are new to Magento 2 the bad news is that it&#8217;s a steep learning curve to get up to the same speed with Magento 2 that you have right now with trusty old Magento1.</p>



<p>Time invested now in setting up an Magento2 development environment is time well spent. I spent the last couple of years dabbling with Magento 2when I had the time &#8211; most of my time was spent getting my Magento 2 Docker environment right and testing migration methods.</p>



<p>Migrating a mature Magento 1 store to Magento 2 is a daunting task. If your store has been around for a long time (mine started life as Magento 1.3 in 2009) no doubt you will have a very customised theme and installed or developed a lot of custom modules and features over the years. You may have 1000&#8217;s of products, 10&#8217;000s of customers and hopefully a lot of order data.</p>



<p>So where do you start? In a perfect world you would start with the official Magento 2 <a href="https://devdocs.magento.com/guides/v2.3/migration/migration-plan.html">migration plan</a> and within half an hour and a few clicks your Magento 1 store is automagically migrated to Magento 2. This fairy tale migration only really works on vanilla Magento 1 installs with no customisation. Having said that as of the latest Magento 2 (2.3.x) the migration process has matured and improved. It is definitely worth familiarising yourself with the migration tool as you probably will need it to migrate some of your Mage 1 data.</p>



<p>For me the migration tool worked perfectly for customer data, but not for products. And of course unless you want to use the basic Magento 2 theme, migrating your Magento 1 theme is simply not possible.</p>



<p>Once you accept the fact that you are going to have to pretty much rebuild your Shop from scratch it&#8217;s time to step back and have a think about how to rebuild 1000&#8217;s of products.</p>



<p>Having worked with Magento 1 for almost 10 years I was used to exporting product data for use in other applications such as Amazon and eBay product feeds, so I thought why not simply <em>export the Magento 1 data in a format that Magento 2 can import.</em></p>



<p>This is not a bad idea, but we are talking about a lot of data &#8211; thousands of products, multi store views, lots of custom attributes. That&#8217;s a pretty big CSV file to manage.</p>



<p>A much better idea is to create a &#8220;skeleton&#8221; export of your Magento 1 product data &#8211; just the bare bones of the products, enough for Magento 2 to import them with enough data to create the product.</p>



<p>Once you have the basic product structure imported simply synchronise the data between MAGE1 and MAGE2! Sounds complex but actually it is pretty easy.</p>



<p>We just need two PHP scripts, one with access to the MAGE1 source files and database and one with access to the MAGE2 installation. The MAGE2 script calls the MAGE1 script to request product data for a SKU. The MAGE1 script extracts all the product data you need into an array returning it in JSON format to MAGE2 which in turn updates the corresponding MAGE2 skeleton product. This is done for each store view.</p>



<p>There are some complications associated with configurable products, attribute sets and product attributes which you need to create and import, but once the basic product is in MAGE2 the synchronisation process works perfectly.</p>



<p>At the time of writing I can synchronise all data for all product types including, pricing, tier pricing, custom attributes and product images.</p>



<p>I can keep Magento 2 in sync with changes or updates to the live Magento 1 shop simply by restoring the latest database backup to my Magento 1 migration database and performing a new product sync.</p>



<p>I started my Migration about 6 weeks ago (May 2019) and having developed the import and sync process I am just about ready to start on a &#8220;live&#8221; dev build of my new Magento 2 store. I don&#8217;t have a new theme yet but there is a lot of work to do getting the catalog ready and customising core templates and code.</p>



<p>I will be updating this blog with my progress and hopefully developing the MAGE1 to MAGE2 export and sync scripts to be made available to download.</p>



<p>Stay tuned to watch this dummy migrate a Magento 1 store to Magento 2!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-migration-for-dummies/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magento 1.9.4.0 and PHP 7 Docker Upgrade</title>
		<link>https://blog.gaiterjones.com/magento-1-9-4-0-and-php-7-docker-upgrade/</link>
					<comments>https://blog.gaiterjones.com/magento-1-9-4-0-and-php-7-docker-upgrade/#comments</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Tue, 11 Dec 2018 10:28:00 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento Upgrade]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=2038</guid>

					<description><![CDATA[With the demise of Magento 1.x on the horizon &#8211; Adobe announced in September 2018 support for version Magento 1 will cease in 2020 new life has been breathed into the product...<a class="more-link" href="https://blog.gaiterjones.com/magento-1-9-4-0-and-php-7-docker-upgrade/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>With the demise of Magento 1.x on the horizon &#8211;<a href="https://magento.com/blog/magento-news/supporting-magento-1-through-june-2020"> Adobe announced in September 2018 support for version Magento 1 will cease in 2020</a> new life has been breathed into the product with version 1.9.4.0.</p>
<p>Magento Open Source v1 covering Magento versions up to 1.9.x is still the eCommerce platform for many small businesses and whilst we all know we need to move to Magento 2 the time and resources required to migrate a successful v1 shop to v2 are for many people prohibitive.</p>
<h1>PHP7</h1>
<p>The original system requirements for Magento 1 were based on PHP5. Magento 1 users were forced to stay with an operating system that distributed PHP5, a PHP upgrade would break Magento. There were <a href="https://inchoo.net/magento/magento-1-official-php-7-2-patches/">unofficial patches</a> available that would make Magento compatible with later versions of PHP but I was always wary of applying unofficial core code changes to my live shops.</p>
<p>In September 2018 Magento made the <a href="https://community.magento.com/t5/News-Announcements/PHP-7-2-Support-Patches-for-Magento-1-are-now-available/m-p/106999#M211">PHP7 patches official</a>, and they were included in the <a href="https://devdocs.magento.com/guides/m1x/ce19-ee114/ce1.9_release-notes.html">December 1.9.4.0 update</a>.</p>
<p>My Magento live Shops were built on Ubuntu Trusty 14.04 LTS which included PHP5. My Docker containers used Trusty as the main OS for my docker images. Now, with the 1.9.4.0 update we can move to current versions of PHP and enjoy the performance benefits of PHP7.x.</p>
<h1>PHUSION</h1>
<p>As a big fan of Ubuntu I usually build my Ubuntu Docker containers using the <a href="https://github.com/phusion/baseimage-docker">Phusion</a> Ubuntu base image. For my Magento2 containers I compile PHP myself so for Magento 1.9.4.0 it was pretty easy to migrate a Magento2 Dockerfile to use for Magento1 on PHP 7.2.</p>
<h1>Upgrade</h1>
<p>Upgrading my Docker Magento1 PHP5 shops to PHP7 was simply a case of applying the 1.9.4.0 upgrade, replacing my old Ubuntu Trusty base image with the Ubuntu Bionic PHP7.2 image and restarting.</p>
<p>I am still testing all my extensions and associated PHP5 Magento application code for functionality but so far the upgrade looks good.</p>
<p>You can find my Magento 1.9.4.0 PHP7.2 image <a href="https://hub.docker.com/r/gaiterjones/magento1/">here</a>. My Dev shop running Magento 1.9.4.0 with PHP7.2 in Docker is <a href="http://magento1.gaiterjones.com/">here</a>.</p>
<p>Github for the source files is here <a href="https://github.com/gaiterjones/docker-magento1-php7">https://github.com/gaiterjones/docker-magento1-php7</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-1-9-4-0-and-php-7-docker-upgrade/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Preparing Magento for the General Data Protection Regulation (GDPR)</title>
		<link>https://blog.gaiterjones.com/preparing-magento-for-the-general-data-protection-regulation-gdpr/</link>
					<comments>https://blog.gaiterjones.com/preparing-magento-for-the-general-data-protection-regulation-gdpr/#comments</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Tue, 15 May 2018 09:47:03 +0000</pubDate>
				<category><![CDATA[GDPR]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[data protection]]></category>
		<category><![CDATA[gdpr]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1889</guid>

					<description><![CDATA[For small businesses in the EU data protection may not be a top priority but the new General Data Protection Regulation (GDPR) has been around since 2016 and will finally...<a class="more-link" href="https://blog.gaiterjones.com/preparing-magento-for-the-general-data-protection-regulation-gdpr/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>For small businesses in the EU data protection may not be a top priority but the new General Data Protection Regulation (GDPR) has been around since 2016 and will finally come into effect on the <strong>25th May 2018</strong>. If you run a Magento eCommerce store here are some compliance issues you may what to consider implementing.</p>
<h1>Move tracking code to Google Tag Manager</h1>
<p>Visitors must give positive consent before their data is used. If you are tracking customer visits with third party tools such as Google Analytics, or customer service modules the user needs to &#8220;opt in&#8221; and give positive consent to permit you to use his data. By using Google Tag Manager you can load all your custom html and javascript tracking code after the user provides consent. Google Tag Manager applies your third party tacking code to all pages making it easier to implement changes.</p>
<h1>Add a cookie consent toolbar</h1>
<p>The GDPR wants you to clearly state what personal data if any you are storing or sharing with third parties. You need to add a cookie consent toolbar to your Magento site that clearly states how and why you use cookies, which third party tracking tools you use and how you share visitor data with third parties. Visitors must give positive consent to this policy &#8211; it is no longer acceptable to say that consent is implied by using the website, users must actively provide consent and your backend logic must implement this. Give visitors as much information as possible and link the cookie consent toolbar to your Data Protection (Privacy) Policy.</p>
<h1>Allow individuals to remove or anonymise data</h1>
<p>You need to be able to provide assurance that admin staff or customers can remove personal data stored in your Magento store on request.</p>
<h1>Allow individuals to opt out from subscriptions</h1>
<p>Make sure your opt-out (and opt-in) to newsletter subscriptions is working.</p>
<h1>Perform Security Testing</h1>
<p>Use the Magento Security scan or Mage Report to provide assurance that your Magento installation is up to date and patched against known security vulnerabilities.</p>
<h1>Update your Data Protection (Privacy) Policy</h1>
<p>Make sure your Data Protection Policy is up to date and available from every page of your Magento site.</p>
<h1>Data Access</h1>
<p>Individuals have the right to ask you of all their personal data and this needs to be a full copy of all data held in Magento. Make sure you are able to fulfill such a request, the GDPR states that this information should be made a available, without charge, within 30 days of the request.</p>
<h1>Create a Compliance Document</h1>
<p>You need to be able to demonstrate a diligent and earnest effort to comply with the GDPR. Create a compliance document that shows how you are meeting the GDPR requirements, the document should include</p>
<ul>
<li>A compliance audit
<ul>
<li>List the steps you have taken to comply with the GDPR</li>
</ul>
</li>
<li>Information audit
<ul>
<li>Audit your systems and clearly state where personal data is stored
<ul>
<li>is it internet facing?</li>
<li>is it shared with third parties?</li>
<li>how is it secured?</li>
<li>Where is it physically stored, servers, pc&#8217;s etc.</li>
</ul>
</li>
</ul>
</li>
<li>Data Protection Policy
<ul>
<li>Include your policy documents</li>
</ul>
</li>
</ul>
<h1>Magento GDPR Compliance Module</h1>
<p>A quick Google for GDPR modules for Magento 1.x and 2.x show there are a few solutions available and most are not free. Two free modules are available</p>
<p>Magento 1 <a href="https://marketplace.magento.com/zero1-zero1-gdpr.html">https://marketplace.magento.com/zero1-zero1-gdpr.html</a></p>
<p>Magento 2 <a href="https://github.com/AdFabConnect/magento2gdpr">https://github.com/AdFabConnect/magento2gdpr</a></p>
<p>I don&#8217;t yet have a live Magento 2 store so have not tested the Magento 2 module. I installed the Zero1 module and found it to contain a couple of bugs which made it unuseable, it appears to be bait to encourage you to purchase the commercial version. The module is fairly simple providing you with a frontend Cookie Consent toolbar, a frontend customer account deletion/anonymisation button and a backend method for anonymising customer data. I took this code and enhanced it with an existing cookie toolbar I had previously developed and the module is free to test and use at <a href="https://github.com/gaiterjones/magento1-gdpr">https://github.com/gaiterjones/magento1-gdpr</a></p>
<p style="text-align: left;">Install the module with modman using modman clone https://github.com/gaiterjones/magento1-gdpr</p>
<p>You will see a new menu item called GDPR in the Configuration -&gt; Customers section.</p>
<p>&nbsp;</p>
<p><figure id="attachment_1898" aria-describedby="caption-attachment-1898" style="width: 457px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" src="https://blog.gaiterjones.com/wp-content/uploads/2018/05/gdpr_module_1.jpg" alt="Magento 1 GDPR Module" width="457" height="468" class=" wp-image-1898" /><figcaption id="caption-attachment-1898" class="wp-caption-text">Magento 1 GDPR Module</figcaption></figure></p>
<p>Adding your Google Tag Manager ID enables the Google Tag Manager code which will insert any tags you have configured into your Magento site.</p>
<p>Enabling the customer account deletion and anonymisation will provide you with two new admin buttons when you view customer information</p>
<p><figure id="attachment_1899" aria-describedby="caption-attachment-1899" style="width: 600px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" src="https://blog.gaiterjones.com/wp-content/uploads/2018/05/gdpr_module_2.jpg" alt="Delete Customer and Anonymise Data" width="600" height="93" class="size-full wp-image-1899" /><figcaption id="caption-attachment-1899" class="wp-caption-text">Backend Delete Customer and Anonymise Data</figcaption></figure></p>
<p>&nbsp;</p>
<p><figure id="attachment_1905" aria-describedby="caption-attachment-1905" style="width: 600px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" src="https://blog.gaiterjones.com/wp-content/uploads/2018/05/gdpr_module_4.jpg" alt="Magento Customer Account Delete" width="600" height="119" class="size-full wp-image-1905" /><figcaption id="caption-attachment-1905" class="wp-caption-text">Magento Customer Account Frontend Delete Option</figcaption></figure></p>
<p>This enables admin staff to delete and anonymise customer and order data and send a confirmation email. In debug mode both these buttons can be tested for functionality. A similar button is activated in the frontend Customer Account section allowing customers to close their account and anonymise their data. You will be notified by email when a customer closes their account.</p>
<p>Debug mode lets you test the module without actually deleting anything, specifically the confirmation emails and notices sent by the module.</p>
<p>You change the look of the Cooke Policy consent bar skin choosing between light or dark skins and you can customise the consent bar text further as you wish.</p>
<p><figure id="attachment_1900" aria-describedby="caption-attachment-1900" style="width: 600px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" src="https://blog.gaiterjones.com/wp-content/uploads/2018/05/gdpr_module_3.jpg" alt="GDPR Magneto Cookie Consent Toolbar" width="600" height="76" class="size-full wp-image-1900" /><figcaption id="caption-attachment-1900" class="wp-caption-text">GDPR Magento Cookie Consent Toolbar</figcaption></figure></p>
<p>You can see the consent bar working at <a href="http://magento1.gaiterjones.com">http://magento1.gaiterjones.com</a></p>
<h1>Seek advice</h1>
<p>Compliance with these new regulations is important, if you are unsure where to begin with GDPR compliance seek professional advice!</p>
<p>The information presented here is a summary of my own personal findings and thoughts on how to improve Magento 1 GDPR compliance and no guarantee whatsoever is provided that the information presented here will ensure GDPR compliance! Use the Magento module at your own risk &#8211; be sure to test it first on your development site.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/preparing-magento-for-the-general-data-protection-regulation-gdpr/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Magento iPhone Mobile Admin with Framework 7</title>
		<link>https://blog.gaiterjones.com/magento-iphone-admin-with-framework-7/</link>
					<comments>https://blog.gaiterjones.com/magento-iphone-admin-with-framework-7/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Thu, 25 Feb 2016 12:58:57 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Mobile Admin]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1453</guid>

					<description><![CDATA[I needed to do some simple Magento admin from my phone when I was away from the office, check orders, refresh caches etc. Using the full backend interface on an...<a class="more-link" href="https://blog.gaiterjones.com/magento-iphone-admin-with-framework-7/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>
I needed to do some simple Magento admin from my phone when I was away from the office, check orders, refresh caches etc. Using the full backend interface on an iPhone to do simple admin tasks is pretty tricky as the backend is not responsive.</p>
<p>
This is an example of a PHP app I created using the <a href="http://framework7.io/">Framework 7</a> HTML framework as a mobile Magento admin interface to refresh caches, check orders, activate customers and set customer groups from a mobile device.</p>
<p>
The customer activation is specifically for use with <a href="https://github.com/Vinai/customer-activation">Vinai/customer-activation</a>.
</p>
<div class="center">
<div class="phone">
			<iframe src="http://magento1.gaiterjones.com/MageAdmin/" frameborder="0" scrolling="no"></iframe></p>
<div class="statusbar"></div>
</p></div>
</p></div></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-iphone-admin-with-framework-7/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magento PayPal Plus Integration</title>
		<link>https://blog.gaiterjones.com/magento-paypal-plus-integration/</link>
					<comments>https://blog.gaiterjones.com/magento-paypal-plus-integration/#comments</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Fri, 12 Feb 2016 18:09:02 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PayPal]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1439</guid>

					<description><![CDATA[If you are an online retailer in Germany using PayPal chances are you have or will be getting a call from PayPal encouraging you to migrate your PayPal payment methods...<a class="more-link" href="https://blog.gaiterjones.com/magento-paypal-plus-integration/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>If you are an online retailer in Germany using PayPal chances are you have or will be getting a call from PayPal encouraging you to migrate your PayPal payment methods to PayPal Plus.</p>
<h2>What is PayPal Plus?</h2>
<p>PayPal introduced PayPal Plus in February 2015 as an all in one payment solution for <strong>online retailers in Germany</strong>. It differs from the normal Magento PayPal methods &#8211; standard and express in a few ways.</p>
<ul>
<li>it uses PayPal’s RESTful API</li>
<li>payment methods are embedded in a checkout &#8220;payment wall&#8221; using an iFrame</li>
<li>multiple payment options are available
<ul>
<li>credit card, direct debit  (Lastschrift), invoice payment (Kaufaufrechnung)</li>
</ul>
</li>
<li>a paypal account is not required to pay</li>
</ul>
<h3>Why is PayPal Plus only available in Germany</h3>
<p>The Germans are very careful when it comes to online data protection, especially where money is concerned. I would guess that the requirement for other payment methods in Germany such as Lastschrift (direct debit) which is a very popular way of paying for all sorts of things in Germany has prompted Paypal to develop a solution tailored specifically toward German consumer requirements.</p>
<h2>How to implement PayPal Plus</h2>
<h3>Magento module</h3>
<p>PayPal will send you an open source PayPal Plus module developed by <a href="http://www.i-ways.net/">iWays</a>. The module will run on Magento CE 1.7 or greater using the default checkout. It also supports some other one page checkout modules.</p>
<p>Module installation is pretty straight forward &#8211; I didn&#8217;t find a GitHub version so created my own as I prefer to manage my module installations with modman.</p>
<p>After installation refresh your caches and a new payment method is created, configure PayPal Plus from system &gt; config &gt; sales &gt; payment methods &gt; paypalplus settings.</p>
<p>The Merchant Country for your store must be set to Germany, PayPal Plus is (at the time of writing) only available in Germany, <em>the module settings will not be displayed if your Merchant Country is not set to Germany</em>.</p>
<p>Configure your PayPal app client and client secret and turn on the sandbox mode for testing.</p>
<p>Note &#8211; if you have OPCACHE enabled and the opcache.save_comments option disabled you Magento will throw an exception with a &#8220;class should have a proper return type&#8221; error. Enable save_comments and reload Apache to continue.</p>
<h2>REST API</h2>
<p>In REST the classic API signature is replaced with <strong>client_ID</strong> and <strong>client secret</strong>. These need to be generated by creating a <a href="https://developer.paypal.com/" target="_blank">PayPal app</a>. To create a PayPal app log in to the PayPal Developer portal with your PayPal account and navigate to the My REST apps page. Click Create App to begin the application-creation process. The app will automatically be in sandbox mode.</p>
<h1>testing</h1>
<p>With valid sandbox API credentials and the module enabled you will see your payment methods have been replaced by the PayPalPlus payment methods templates (<em>base/default/template/paypalplus</em>). The template embeds the PayPal Plus payment wall iFrame into your checkout page including the 4 standard payment methods.</p>
<p>Note &#8211; if you have already extended and customised the base payment method template it has now also been extended by the iWays modules templates. You will need to extend the iWays templates and modify them with your customisations.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1434 size-large" src="https://blog.gaiterjones.com/wp-content/uploads/2016/02/paypalplus-payment-iframe-de-620x310.jpg" alt="" width="550" height="275" srcset="https://blog.gaiterjones.com/wp-content/uploads/2016/02/paypalplus-payment-iframe-de-620x310.jpg 620w, https://blog.gaiterjones.com/wp-content/uploads/2016/02/paypalplus-payment-iframe-de-440x220.jpg 440w, https://blog.gaiterjones.com/wp-content/uploads/2016/02/paypalplus-payment-iframe-de-550x275.jpg 550w, https://blog.gaiterjones.com/wp-content/uploads/2016/02/paypalplus-payment-iframe-de.jpg 700w" sizes="(max-width: 550px) 100vw, 550px" /></p>
<p>The default payment wall is white as shown above, at the time of writing there are no options available to change the style. colours etc of the payment wall.</p>
<p>The module quite cleverly detects your existing PayPal payment methods &#8211; express, standard etc. And knows to replace them with PayPal plus. Your other payment methods will appear below the PayPal Plus options nicely styled to match the payment wall. You should leave all your existing PayPal payment options enabled, should there be any problems with PayPal Plus your shop will automatically fall back to your normal payment methods.</p>
<p>You can also integrate your other payment methods directly into the payment wall by enabling them in the  &#8220;allow third party modules&#8221; module settings.</p>
<p>In sandbox mode you can now test out all the payment options. To test Kauf auf Rechnung (Invoice) you need to set the &#8220;Show PuI in Sandbox&#8221; option to Yes in the module development settings options. PuI is Payment Upon Invoice &#8211; a rather literal translation of the German Kauf auf Rechnung.</p>
<h1>Kauf auf Rechnung</h1>
<p>PayPal Plus lets customers order and pay by invoice. The merchant receives the payment directly from Paypal like any other PayPal transaction. The goods can be shipped as soon as the merchant is paid by PayPal and the customer has up to 30 days to pay the invoice, paying the outstanding amount directly to PayPal.</p>
<p>Many online retailers shy away from offering this kind of payment option due to the problems arising from unpaid invoices and chasing debtors for payment but with PayPal taking over this responsibility there is no reason (apart from the PayPal transaction fees) to not offer it.</p>
<p>One problem with this option is making sure that the customer knows to pay the outstanding invoice amount to Paypal and not the Merchant. You will notice when you test payments that the module shows all payment methods on order confirmations as &#8220;PayPal, Lastschrift, Kreditkarte, Kauf auf Rechnung&#8221;.</p>
<p>At the time of writing there is actually no way to distinguish between PayPal Plus Paypal, Kreditkarte or Lastschrift payments. You can however identify invoice Kauf auf Rechnung payments.</p>
<p>If you look at a payment made with Kauf auf Rechnung you will see the order has a ppp_pui_instruction_type object set to &#8220;PAY_UPON_INVOICE&#8221;, this lets you check for invoice payments using something like</p>
<pre class="brush: php; title: ; notranslate">
	if($_order-&gt;getPayment()-&gt;getData('ppp_pui_instruction_type')=='PAY_UPON_INVOICE')
	{
		$_isPUI=true;
	}
</pre>
<p>This is useful because Paypal recommends you make sure the customer is aware that the payment must be made to PayPal. By identifying an invoice payment we can customise the information shown to the customer when they pay using Kauf auf Rechnung.</p>
<p>For example we can customise the new order email to show Kauf auf Rechnung as the payment method, and add the recommended PayPal invoice payment text to the payment information.</p>
<p><figure id="attachment_1436" aria-describedby="caption-attachment-1436" style="width: 362px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="size-full wp-image-1436" src="https://blog.gaiterjones.com/wp-content/uploads/2016/02/kauf-auf-rechnung-sales-email.jpg" alt="Kauf auf Rechnung sales email" width="362" height="414" /><figcaption id="caption-attachment-1436" class="wp-caption-text">Kauf auf Rechnung sales email</figcaption></figure></p>
<p>&nbsp;</p>
<p>Similarly we can add the same information to the checkout success page.</p>
<p>To customise your email templates you need to add a new handler to your layout.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;layout version=&quot;0.1.0&quot;&gt;
	&lt;!-- sales payment info handler --&gt;
	&lt;my_sales_email_payment_info&gt;
		&lt;block type=&quot;core/template&quot; name=&quot;addPaymentInfo&quot; template=&quot;my/sales/email/payment/info.phtml&quot; /&gt;
	&lt;/my_sales_email_payment_info&gt;
&lt;/layout&gt;
</pre>
<p>in your sales/email/payment/info.phtml template add the logic and information you want to display in the sales email.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

$_order = $this-&gt;getOrder();
$_isPUI=false;

	
	if($_order-&gt;getPayment()-&gt;getData('ppp_pui_instruction_type')=='PAY_UPON_INVOICE')
	{
		$_isPUI=true;
	}
	

?&gt;
&lt;?php if($_isPUI):?&gt;
		&lt;p&gt;YOUR CUSTOM TEXT&lt;/p&gt;
&lt;?php endif;?&gt;
</pre>
<p>You can now call this handler from anywhere in your sales email templates using</p>
<pre class="brush: php; title: ; notranslate">
{{layout handle=&quot;my_sales_email_payment_info&quot; order=$order}}
</pre>
<p>to display the information from your template. We can do exactly the same for the checkout success page, you just need to retrieve the order object first.</p>
<pre class="brush: php; title: ; notranslate">
$_order = Mage::getModel('sales/order')-&gt;load(Mage::getSingleton('checkout/session')-&gt;getLastOrderId());
</pre>
<h1>Conclusions</h1>
<p>Considering Paypal used to belong to eBay, and eBay now owns Magento it&#8217;s likely that PayPal plus will become part of the Magento core code at some point in the future. The iWays module is well written and works well &#8211; it will need some customisation to fit into your own specific requirements especially if you have already customised your payment method templates. Time will tell how invoice payments work&#8230;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-paypal-plus-integration/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Magento Dev Folder</title>
		<link>https://blog.gaiterjones.com/magento-dev-folder/</link>
					<comments>https://blog.gaiterjones.com/magento-dev-folder/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Wed, 21 Oct 2015 16:43:27 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1421</guid>

					<description><![CDATA[The Magento 1.9.2.1 release source code included the Magento Test Framework files. The Magento Test Framework (MTF) is a tool that can run automated functional tests, to assist in the development...<a class="more-link" href="https://blog.gaiterjones.com/magento-dev-folder/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>The Magento 1.9.2.1 release source code included the Magento Test Framework files. <a href="http://merch.docs.magento.com/ce/user_guide/Magento_Community_Edition_User_Guide.html#magento/magento-test-framework.html%3FTocPath%3DSystem%2520Operations%7CSystem%2520Tools%7C_____6">The Magento Test Framework (MTF)</a> is a tool that can run automated functional tests, to assist in the development and implementation of Magento modules.</p>
<p>The test files are included in the Magento root /dev folder.  The files are included for development purposes and might reveal your passwords or other sensitive information when accessed with malicious intent. The /dev directories and files are not protected by default and according to Magento, &#8220;these tests are not supposed to end up on production servers&#8221;.</p>
<p><strong>Do not install these files in a production environment.</strong></p>
<p>Delete the /dev/ folder from your production server.<br />
Delete the /dev/ folder after upgrading to a newer version of Magento.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-dev-folder/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magento Google Pagespeed js,css,htmlminify Optimisation</title>
		<link>https://blog.gaiterjones.com/magento-google-pagespeed-jscsshtmlminify-optimisation/</link>
					<comments>https://blog.gaiterjones.com/magento-google-pagespeed-jscsshtmlminify-optimisation/#comments</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Fri, 28 Aug 2015 10:24:32 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Pagespeed]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1407</guid>

					<description><![CDATA[Google PageSpeed is a family of tools by Google Inc, designed to help a website’s performance optimisations. Page speed is important for Magento customers, Google search and for SEO. The...<a class="more-link" href="https://blog.gaiterjones.com/magento-google-pagespeed-jscsshtmlminify-optimisation/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>Google PageSpeed is a family of tools by Google Inc, designed to help a website’s performance optimisations. Page speed is important for Magento customers, Google search and for SEO. The question is, is it possible to achieve a 100/100 rating with the Google Pagespeed insight tool?</p>
<p><figure id="attachment_1409" aria-describedby="caption-attachment-1409" style="width: 336px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-1409 size-full" src="https://blog.gaiterjones.com/wp-content/uploads/2015/08/pagespeed-100-100.png" alt="pagespeed-100-100" width="336" height="98" /><figcaption id="caption-attachment-1409" class="wp-caption-text">The illusive Pagespeed insights 100/100 rating.</figcaption></figure></p>
<p>&nbsp;</p>
<h2>Magento Pagespeed 100/100 rating</h2>
<p>Well unfortunately due to some core Magento design issues I think the answer is no, <strong>BUT</strong> &#8211; you should be able to achieve a green rating of at least 90 to 95.</p>
<p>The <a href="https://blog.gaiterjones.com/5-ways-to-improve-magento-page-speed/" target="_blank">last time I worked on Pagespeed optimisation</a> I achieved an <em>85</em> desktop rating for a Magento landing page, today I achieved a <em>93</em> rating by further optimising JavaScript, CSS and HTML.</p>
<h2>Magento Pagespeed Optimisation</h2>
<p>Google Pagespeed insights targets 5 main optimisation categories</p>
<ul>
<li>Stylesheets optimizations</li>
<li>Javascript files optimizations</li>
<li>Images optimizations</li>
<li>HTML optimizations</li>
<li>Tracking activity filters</li>
</ul>
<p>When you run the <a href="https://developers.google.com/speed/pagespeed/insights/" target="_blank">insights tool</a> these are the areas that will be looked at and reported on. Google will clearly tell you where your Magento store needs improvement, it is likely you will need to look at both your server configuration and Magento to address Pagespeed issues found by Magento.</p>
<p>Problems such as web server <a href="https://blog.gaiterjones.com/5-ways-to-improve-magento-page-speed/">compression not enabled, browser caching not enabled</a> and server response times will require changes to your web server setup or core server configuration.</p>
<p><a href="https://blog.gaiterjones.com/5-ways-to-improve-magento-page-speed/" target="_blank">Image optimisation</a> is achievable with free image tools.</p>
<p>&nbsp;</p>
<p>CSS, Javascript and HTML optimisation and full page caching requires some extra Magento tweeking.</p>
<p>&nbsp;</p>
<h2>Magento CSS, Javascript and HTML optimisation</h2>
<p>Depending on your Magento theme there can be a lot of javascript and CSS includes in your source html. Best practice is to configure Magento to combine CSS and Javascript into one file. You do this by enabling JavaScript and CSS merge in <em>System -&gt; Configuration -&gt; Advanced -&gt; Developer</em>. You should also then install <a href="https://github.com/fooman/speedsteradvanced" target="_blank">Fooman Speedster Advanced</a> which will also minify JavaScript and CSS.</p>
<p>This process works well,  but by default Magento will insert all your JS and CSS into the header of the rendered webpage. The best practice for any CSS or JavaScript that might block the rendering of the webpage (DOM) until they are loaded is to move the JavasScript to the footer of the page.</p>
<p>The Google Pagespeed insights tool will quite likely tell you that you have render blocking JS and CSS.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1410" src="https://blog.gaiterjones.com/wp-content/uploads/2015/08/pagespeed-render-blocking.png" alt="pagespeed-render-blocking" width="567" height="114" srcset="https://blog.gaiterjones.com/wp-content/uploads/2015/08/pagespeed-render-blocking.png 567w, https://blog.gaiterjones.com/wp-content/uploads/2015/08/pagespeed-render-blocking-440x88.png 440w, https://blog.gaiterjones.com/wp-content/uploads/2015/08/pagespeed-render-blocking-550x111.png 550w" sizes="(max-width: 567px) 100vw, 567px" /></p>
<p>There are ways of customising the layout of your theme to move certain JS and CSS source files to the footer, in practice this can be complicated because you need to ensure that the loading order of your JavasScript files does not change so that scripts or plugins with dependencies on other JS libraries still run.</p>
<p>The guys at <a href="https://github.com/mediarox/pagespeed" target="_blank">Mediarox </a>have developed a neat solution that simply does a big cut and paste on your html code, finding all JS script and CSS elements, removing them from the header of the html and placing them in the footer.</p>
<p>I was pretty skeptical at first as to how effective this would be, but after installing the module and testing it I couldn&#8217;t see any major problems. The Mediarox module doesn&#8217;t minify html which was something I had been working on, so I <a href="https://github.com/gaiterjones/magento-pagespeed" target="_blank">integrated my minify html module into theirs and forked it on github.</a></p>
<p>My minify module uses the minify code from the <a href="https://code.google.com/p/minify/" target="_blank">Minify </a>project. My version of magento-pagespeed is adapted to work with a modified <a href="https://github.com/gaiterjones/Lesti_Fpc" target="_blank">Lestis full page cache</a> and the Mediarox JS and CSS modules.</p>
<h2>Install magento-pagespeed</h2>
<p><em>modman clone https://github.com/gaiterjones/magento-pagespeed</em></p>
<p>Install the module and configure it at <em>System -&gt; Configuration -&gt; Advanced -&gt; Pagespeed.</em> Here you can enable or disable HTML Minify, JS and CSS optmisation individually. I also added an extra option to disable all modules for logged in users.</p>
<p>If you want to exclude any JS scripts from optimisation enable the exclude list option and add a regex to identify the script.</p>
<p>Magento-pagespeed optimises Magento JS, CSS and html, I then cache the optimised html with Lesti_FPC. When implemented correctly you will see debug comments in the source of the Magento webpage indicating which Pagespeed (PS) module optimisations took place:</p>
<p>&lt;!&#8211; +PS JS 28-08-2015 07:50:04 4ms &#8211;&gt;<br />
&lt;!&#8211; +PS MIN_HTML 28-08-2015 07:50:04 30ms &#8211;&gt;<br />
&lt;!&#8211; +FPC 28-08-2015 07:50:04 &#8211;&gt;</p>
<p>Here we see PS JS and PS MIN_HTML are enabled. The JS optimisation took 4ms, the HTML minify code took 30ms. the html was then cached by Lest_FPC.</p>
<p>Moving the JS to the footer really helps to improve render blocking and this is reflected by the <a href="http://dev.gaiterjones.com/magento/" target="_blank">Pagespeed Insights tool running on my dev site </a>:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-1411" src="https://blog.gaiterjones.com/wp-content/uploads/2015/08/dev-page-speed-insights-581x620.png" alt="dev-page-speed-insights" width="550" height="587" srcset="https://blog.gaiterjones.com/wp-content/uploads/2015/08/dev-page-speed-insights-581x620.png 581w, https://blog.gaiterjones.com/wp-content/uploads/2015/08/dev-page-speed-insights-412x440.png 412w, https://blog.gaiterjones.com/wp-content/uploads/2015/08/dev-page-speed-insights-550x587.png 550w, https://blog.gaiterjones.com/wp-content/uploads/2015/08/dev-page-speed-insights.png 718w" sizes="(max-width: 550px) 100vw, 550px" /></p>
<p>We can see that there are still render blocking issues and you need to experiment with CSS optimisation for your theme, a side effect of moving all CSS to the footer is that your page may load with some default styles until the CSS is loaded, leading to an ugly transition from unstyled to styled elements on the page. You can see how highly these issues are ranked by Google, render blocking CSS and Javascript that could be further compressed by 2% (1.3 Kb) is costing me 7 points on the insight scale and stopping me from getting to 100/100.</p>
<p>Could I get to 100/100, probably but 93 is still a pretty good result I think.</p>
<p>&nbsp;</p>
<h3>Resources</h3>
<ul>
<li><a href="https://github.com/gaiterjones/magento-pagespeed" target="_blank">magento-pagespeed</a>
<ul>
<li>js, css, html optimisation module adapted from <a href="https://github.com/mediarox/pagespeed" target="_blank">Mediarox Pagespeed</a></li>
<li>installation
<ul>
<li>modman clone https://github.com/gaiterjones/magento-pagespeed</li>
</ul>
</li>
</ul>
</li>
<li><a href="https://github.com/gaiterjones/Lesti_Fpc" target="_blank">Lesti FPC</a>
<ul>
<li>adapted to work with magento-pagespeed</li>
<li>installation
<ul>
<li>modman clone https://github.com/gaiterjones/Lesti_Fpc</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-google-pagespeed-jscsshtmlminify-optimisation/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			</item>
		<item>
		<title>Magento Lowest Group Product Price Including Tier Prices</title>
		<link>https://blog.gaiterjones.com/magento-lowest-group-product-price-including-tier-prices/</link>
					<comments>https://blog.gaiterjones.com/magento-lowest-group-product-price-including-tier-prices/#respond</comments>
		
		<dc:creator><![CDATA[PAJ]]></dc:creator>
		<pubDate>Mon, 17 Aug 2015 15:04:41 +0000</pubDate>
				<category><![CDATA[Magento]]></category>
		<guid isPermaLink="false">http://blog.gaiterjones.com/?p=1399</guid>

					<description><![CDATA[In Magento you can get price data for a product with $this-&#62;getPriceHtml($_product, true). The boolean true also returns information for the lowest price of the product. For configurable and simple products...<a class="more-link" href="https://blog.gaiterjones.com/magento-lowest-group-product-price-including-tier-prices/" title="Continue reading">Continue reading</a>]]></description>
										<content:encoded><![CDATA[<p>In Magento you can get price data for a product with <em>$this-&gt;getPriceHtml($_product, true)</em>. The boolean true also returns information for the lowest price of the product. For configurable and simple products this will give you the lowest price including tier prices. For grouped products however it will give you the lowest unit price of the associated simple products.</p>
<p>If you want to show your grouped product lowest price including tier prices you need to modify price.phtml in <em>app/design/frontend/yourtheme/default/template/catalog/product</em></p>
<p>If you edit price.phtml and scroll to the bottom you will see the logic for grouped products and the getDisplayMinimalPrice html. Add the code below just above the line with <em>$showMinPrice = $this-&gt;getDisplayMinimalPrice();</em></p>
<p>This will get all the prices associated with the grouped product and set the lowest to be displayed as the miminal or &#8220;starting as&#8221; price. You can see this working here http://dev.gaiterjones.com/magento/catalogsearch/result/?q=1114 where the starting price is 99 &#8211; the lowest tier price. Without this modification the starting price is shown as the lowest unit price.</p>
<pre class="brush: php; title: ; notranslate">
			// get lowest price from tiers

			$_groupedProductChildPrices=array();

			// get child products of grouped product
			//
			$_childProductIds = $_product-&gt;getTypeInstance()-&gt;getChildrenIds($_product-&gt;getId());

			foreach ($_childProductIds as $_ids) {

				foreach ($_ids as $_id) {

					$_childProduct = Mage::getModel('catalog/product')-&gt;load($_id);

					// get all associated group product pricing, including tier prices
					$_groupedProductChildPrices&#x5B;] = $_childProduct&#x5B;'price'];

						if($_childProduct-&gt;getTierPrice() != NULL) {
							foreach($_childProduct-&gt;getTierPrice() as $tier){
								$_groupedProductChildPrices&#x5B;]= $tier&#x5B;'price'];
							}
						}

				}
			}

			// set lowest price and reset minimal price variables
			//
			$_lowestGroupProductPrice=min($_groupedProductChildPrices);

			$_minimalPriceValue = $_store-&gt;roundPrice($_store-&gt;convertPrice($_lowestGroupProductPrice));
			$_minimalPrice = $_taxHelper-&gt;getPrice($_product, $_minimalPriceValue, $_simplePricesTax);
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.gaiterjones.com/magento-lowest-group-product-price-including-tier-prices/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
