Magento – Inchoo http://inchoo.net Magento Design and Magento Development Professionals - Inchoo Thu, 08 Dec 2016 12:22:40 +0000 en-US hourly 1 https://wordpress.org/?v=4.6.1 Add Pagination to Custom Collection in Magento http://inchoo.net/magento/pagination-custom-collection/ http://inchoo.net/magento/pagination-custom-collection/#respond Thu, 08 Dec 2016 12:22:14 +0000 http://inchoo.net/?p=28165 If you are starting with Magento, just like I am, you might run into the issue of adding pagination to your table, list of products or anything else you want to list on frontend. Magento allows you to create your own pagination with adjustable built-in options. This tutorial will show you how to create pagination for a custom collection...

The post Add Pagination to Custom Collection in Magento appeared first on Inchoo.

]]>
If you are starting with Magento, just like I am, you might run into the issue of adding pagination to your table, list of products or anything else you want to list on frontend. Magento allows you to create your own pagination with adjustable built-in options. This tutorial will show you how to create pagination for a custom collection in Magento.

This is how final custom pagination looks like:
magento pagination

I created Education module inside Inchoo package with Ticket model.

magento pagination

STEP 1

First of all create custom action to access your content. Add a custom action inside your controller:

public function paginationAction()
{
    $this->loadLayout();
    $this->renderLayout();
}

STEP 2

In your xml (app > design > frontend > rwd > default > layout > your_layout.xml) inside “layout” node add the node referencing your custom action. If you are using different or custom theme your path might be different than “rwd > default”.

“education/pagination” – where “education” is the name of your module, “pagination” is name of your block inside pp/code/local/Inchoo/Education/Block folder, my block is named Pagination.php.

<education_index_pagination>
  <reference name="content">
    <block name="collection_ticket" type="education/pagination" template="education/pagination.phtml"/>
  </reference>
</education_index_pagination>

Magento pagination

STEP 3

Create class that will extend Magento’s default product list toolbar so you can alter default behavior. Inside app/code/local/Inchoo/Education/Block create Toolbar.php

class Inchoo_Education_Block_Toolbar extends Mage_Catalog_Block_Product_List_Toolbar
{
    public function getPagerHtml()
    {
        $pagerBlock = $this->getLayout()->createBlock('page/html_pager');
        if ($pagerBlock instanceof Varien_Object)
        {
            /* here you can customize your toolbar like h*/
            $pagerBlock->setAvailableLimit($this->getAvailableLimit());
            $pagerBlock->setUseContainer(false)
                ->setShowPerPage(false)
                ->setShowAmounts(false)
                ->setLimitVarName($this->getLimitVarName())
                ->setPageVarName($this->getPageVarName())
                ->setLimit($this->getLimit())
                ->setCollection($this->getCollection());
            return $pagerBlock->toHtml();
        }
        return '';
    }
}

STEP 4

Create your block inside your module’s block, in this case app/code/local/Inchoo/Education/Block. I named this block Pagination, you can call it however you want to, but then you have to be careful about calling it in your xml.

class Inchoo_Education_Block_Pagination extends Mage_Core_Block_Template
{
    public function __construct()
    {
        parent::__construct();
        //set your own collection (get data from database so you can list it)
        $collection = Mage::getModel('education/ticket')->getCollection();
        $this->setCollection($collection);
    }
    protected function _prepareLayout()
    {
        parent::_prepareLayout();
        //this is toolbar we created in the previous step
        $toolbar = $this->getLayout()->createBlock('education/toolbar');
        //get collection of your model
        $collection = Mage::getModel('education/ticket')->getCollection();
        //this is where you set what options would you like to  have for sorting your grid. Key is your column in your database, value is just value that will be shown in template
        $toolbar->setAvailableOrders(array('created_at'=> 'Created Time','id'=>'ID'));
        $toolbar->setDefaultOrder('id');
        $toolbar->setDefaultDirection("asc");
        $toolbar->setCollection($collection);
        $this->setChild('toolbar', $toolbar);
        $this->getCollection()->load();
        return $this;
    }
 
    //this is what you call in your .phtml file to render toolbar
    public function getToolbarHtml()
    {
        return $this->getChildHtml('toolbar');
    }
}

STEP 5

Inside app > design > frontend > rwd > default > template (or change rwd > default to your theme path) create pagination.phtml (as you stated in step 2):

<?php $collection = $this->getCollection();  ?>
    <div class="page-title">
        <h1><?php echo $this->__('My Collection') ?></h1>
    </div>
<?php echo $this->getToolbarHtml(); ?>
    <table class="data-table" id="my-custom-table">
        <thead>
        <tr>
            <th><?php echo $this->__('Title') ?></th>
            <th><span class="nobr"><?php echo $this->__('Created at') ?></span></th>
        </tr>
        </thead>
        <tbody>
        <?php $_odd = ''; ?>
        <?php foreach ($collection as $item): ?>
            <tr>
                <td><span class="nobr"><?php echo $item->getSubject(); ?></span></td>
                <td><?php echo $this->formatDate($item->getCreatedAt()) ?></td>
            </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
<?php echo $this->getToolbarHtml(); ?>

*Custom pagination

If you want to create your own custom pagination without using Magento’s Toolbar, after you finished step 1 and step 2 inside app/code/local/Inchoo/Education/Block create Pagination.php.

class Inchoo_Education_Block_Pagination extends Mage_Core_Block_Template
{
    public function __construct()
    {
        parent::__construct();
        $collection = Mage::getModel('education/ticket')->getCollection();
        $this->setCollection($collection);
    }
    protected function _prepareLayout()
    {
        parent::_prepareLayout();
        $pager = $this->getLayout()->createBlock('page/html_pager', 'custom.pager');
        $pager->setAvailableLimit(array(5=>5,10=>10,20=>20,'all'=>'all'));
        $pager->setCollection($this->getCollection());
        $this->setChild('pager', $pager);
        $this->getCollection()->load();
        return $this;
    }
    public function getToolbarHtml()
    {
        return $this->getChildHtml('pager');
    }
}

After you did this, skip step 3 and 4 and go straight to step 5 to create .phtml. When you’re done with that you’ll get something like this:

magento pagination

The post Add Pagination to Custom Collection in Magento appeared first on Inchoo.

]]>
http://inchoo.net/magento/pagination-custom-collection/feed/ 0
Road to the Code with Less Bugs? http://inchoo.net/magento/programming-magento/less-bugs/ http://inchoo.net/magento/programming-magento/less-bugs/#respond Tue, 06 Dec 2016 12:31:09 +0000 http://inchoo.net/?p=28220 I think that writing any non-trivial software without introducing bugs is close to impossible. World is complex, data used in programs is complex, handling all possible interactions of our code with said data is hard. There are probably bugs even in the short script that is accompanying this article. Sounds dark? Maybe it shouldn’t. Everybody...

The post Road to the Code with Less Bugs? appeared first on Inchoo.

]]>
I think that writing any non-trivial software without introducing bugs is close to impossible. World is complex, data used in programs is complex, handling all possible interactions of our code with said data is hard. There are probably bugs even in the short script that is accompanying this article. Sounds dark? Maybe it shouldn’t.

Everybody who writes software is creating bugs, it is inevitable.

There is huge amount of research about what causes bugs and there are various results and conclusions.

My interpretation of the surface reading is that number of “bugs per line” is scaling in some way with total number of lines in program. For example, program with 1000 lines will have one bug, program with 2000 lines will have 3 bugs, and program with 50 000 lines will have 100 bugs. Personally, I feel like that is true as it kind of matches my experience. Programmers mostly agree about this.

Another finding is that programs with more restriction about who (who = which code) and when (under which conditions) can modify some state also have less bugs. (Scientists say!) I agree, because it also matches my experience. In long functions with many variables or classes with lots of properties which are used later for logic there are more bugs. Especially if we are writing code under assumption that objects will always be in some finite amount of states that we accounted for or that variables will be configured in a certain way. (error calling Method() on null, index not found, Varien_Object returning null on getPropertty() instead of giving error)

There is also something called cyclomatic complexity (amount of logical branches code can take).
I hope there are least some of us who did not experience that feeling of dread upon reading logs and seeing an exception occurred in the middle of 500+ line method of class on a line which starts somewhere after character number 52…

So, if we could somehow:

  • Make objects/variables which are always in valid state. Maybe by using static declarations, there will be another article about how PHP7 is improvement over PHP5 here.
  • Make sections of code smaller and easier to understand. Perhaps by reducing number of branches and loops.
  • Reduce overall amount of code. Perhaps by using higher level constructs, so we write less code.

Honestly, I am not really sure if such ideas will really work. So far, my feeling is that it helps a lot. Maybe it will help more when I get more experience, maybe it is actually worse and I lack knowledge/skill to see it.

But I can see there is lot of space for improvement. And I am very confident that we should at least examine some of the options and see how they work in practice.

So, in this teaser post, let’s do something everybody enjoys!

Parsing CSV files, and doing some analysis on that data!!!

Input: CSV with UN employment data. Rows with years, work sectors and number of people working in each sector per given year (in 2002, there were 100 000 people working for the Government).

Output: Statistic with percentages that show how many people worked in some sector for given year, and how those percentages changed over years.

For the first post, we are going simple, only showing data for one country and for both genders.
That gives us excuse to introduce generators. 🙂 Our goal is to show the table in HTML (ugly looking table but we won’t be concerned with styling, only with filling table with proper data).
Performance is secondary goal, and it will not be tackled until we are satisfied with the code quality. Corectness and crash-resistance are primary goals!

I will quickly show uses of some of the concepts and we will try to improve code as series go on.
Not sure where we will end and if functional style is good fit for the problem overall but let’s see where it will take us. Along the way, we’ll also see if functional in PHP will be easier to understand than the same thing implemented in OOP way or old-school imperative approach.

Code is on github

https://github.com/grizwako/broadening_horizons

We will start with closures.

Closures are good for encapsulating data, without going full on object-oriented.
http://php.net/manual/en/functions.anonymous.php
http://culttt.com/2013/03/25/what-are-php-lambdas-and-closures/
http://fabien.potencier.org/on-php-5-3-lambda-functions-and-closures.html

Goes like this – you create some function which will have some data saved, and you can later call that function (with new parameters too).

Example function is accessing values of CSV data rows by column names defined in CSV header line.

function mappedRow(data-row, column-map)
 
$entry = mappedRow(
    [4, 'bla@blo.com'],
    ['id' => 0, 'email' => '1]
);
 
echo $entry('email'); //bla@blo.com

Implementation:

/**
 * Creates dictionary-like closure which accesses array values by string keys.
 * So instead of $row[$map['key'] you can use $row('key')
 * @param $row
 * @param $columnMap
 * @return Closure
 */
function mappedRow($row, $columnMap)
{
    if(empty($columnMap)) {
        throw new LogicException('Column map must not be empty! Hint: reverse header row to use as column map');
    }
 
    //returns function which accepts one parameter and has saved values for $row and $columnMap
    return function($key = null) use ($row, $columnMap) {
        return $row[$columnMap[$key]];
    };
}

 

Second useful thing: generators!

http://php.net/manual/en/language.generators.overview.php
Generators are like iterators/array, but differ in the fact that they will give you data only when they are told to do so. We can use generator to read huge CSV file line by line, and we only take lines that we need.

For now, you can think of keyword yield as “feed (return) value into caller, but save state of generator function for when it is called next”.

Generator is somewhat like closure, it can also have some local variables saved for later usages.

/**
 * Lazily read CSV file
 *
 * @param $filename
 * @param string $split
 * @param int $maxLineLength
 * @param array $columnMap
 * @return Generator
 * @throws Exception
 */
function mappedCsvGenerator($filename, $split = ',', $maxLineLength = 0, $columnMap = [])
{
    $fileHandle = fopen($filename,'r');
    if(FALSE === $fileHandle) {
        throw new Exception('Could not open file: '.$filename);
    }
    while (($data = fgetcsv($fileHandle, $maxLineLength, $split)) !== FALSE) {
        if(empty($columnMap)) {
            //Moves array pointer to next row
            $columnMap = array_flip($data);
            continue;
        }
 
        yield mappedRow($data, $columnMap);
    }
}

Let’s create instance of a generator; we are still not asking it to read data from CSV.

$rows = mappedCsvGenerator('un_employment_1980-2008.csv');

Next, we will not need all rows from CSV, only a few. Let’s pretend that we do not have the database system in which we can import data. Filter rows as we are only interested in data for one country and for both genders.

It would be wonderful if we could somehow delay reading of the CSV rows to the point when we actually need them.

Luckily, Nikita Popov created a really nice library that helps with using lazy collections. (Also, he is one behind a lot of cool stuff that PHP got recently. Generators included.)
About Nikita: https://nikic.github.io/aboutMe.html
Lazy collections: https://github.com/nikic/iter

$stats = iter\filter(
    function($row) {
        return $row('Country or Area') === 'Croatia'
		&& $row('Sex') === 'Total men and women';
    },
    $rows
);

CSV file is still unread!! We did not actually use the values, we only specified that the values we will use will have to satisfy some condition. Instead of creating an array and then inserting rows from some foreach, we only said that $stats will give us rows that satisfy some condition when we ask for them.

Grouping values with common higher order functions

Next step, I think that we will need to group values by work sector and year if we want to display them in a table.

Higher order just means function that accepts other function in a parameter, nothing actually fancy.
Word group sounds like it should be a function, but there is not one such in Nikic’s library.
Some searching will bring us to: https://github.com/lstrojny/functional-php

It has group, it seems it can only do one level of grouping with one call. There is some advanced stuff in the library Lars made so maybe we can do some mumbo-jumbo when we revisit the code and refactor it to compose function that will group by two levels.

First we group by work sector (subclassification). And then we do something that is actually elegant, but looks weird if you are not used to it. Map is kind of like foreach, but it is not supposed to modify collection it is iterating over. Think of it like: map = foreach item: call function “a” and return items function “a” returned.

Function “a” will accept one item from the collection, do something and return one item. Map will return array of all items function “a” returned.

$statsBySubclassification = group($stats, function($row){
    return $row('Subclassification');
});
 
$statsBySubclassificationAndYear = map($statsBySubclassification, function($subclass) {
    $indexed = reindex($subclass, function($row) {
        return (int)$row('Year');
    });
    return map($indexed, function($row) {
        return (float)$row('Value');
    });
});

So, with our grouped entries we will call function (which accepts all entries that fall under one Subclassifiction). That function will first create a new array which will have “Year” of entry as a key and after that go over all elements and get ‘Value’ only for each row.

Something like:

$statsBySubclassificationAndYear = [];
foreach($statsBySubclassification as $subclassifiction => $items) {
    foreach($items as $item) {
    	//PHP has autovivification, so we dont have to worry about creating proper array keys
        $statsBySubclassificationAndYear[$subclassifiction][(int)$item('Year')] = (float)$item('Value');
    }
}

Honestly, in PHP foreach seems easier at first, even to me.
In some made up functional language, we would have this sort of thing:

statsBySubclassificationAndYear = map(statsBySubclassification, (subclass) =>
    indexed = reindex(subclass, (row) => (int)row('Year'))
    map(indexed, (row) => (float)row('Value'))
)

 

Displaying grouped data as a table:

<?php
$totalByYear = $statsBySubclassificationAndYear['Total.'];
$years = array_keys($totalByYear);
sort($years); // Why is this bad style?
 
//now, lets build table that will show us suclassifications percentages over years
// I feel that foreach is cleaner here, because we are mutating some object
$table = new SimpleXMLElement('<table/>');
$head = $table->addChild('tr');
$head->addChild('th', 'Subclassification');
foreach($years as $year) {
    $head->addChild('th', $year);
}
 
foreach($statsBySubclassificationAndYear as $subclass => $byYear) {
    $tableRow = $table->addChild('tr');
    $tableRow->addChild('td', $subclass);
    $percentage = 0;
    //we are relying on $years that was defined too far from where we are using it
    foreach($years as $year) {
        if(array_key_exists($year, $byYear)) {
            // can this part of code be improved by using functional style?
            // we are relying on $percentage from last iteration, stuff like that causes bugs
            $tempPerc = 100 *($byYear[$year] / $totalByYear[$year]);
            $delta = $tempPerc - $percentage;
            $percentage = $tempPerc;
            $procFormat = number_format($percentage, 2);
            $deltaFormat = number_format($delta, 2);
            $tableRow->addChild('td', $procFormat)->addChild('p', $deltaFormat);
        } else {
            $tableRow->addChild('td', ' - ');
        }
    }
}
 
?>
 
<style>
    table, th, td {
        border: 1px solid black;
    }
    p {
        color: blueviolet;
    }
</style>
 
Percentages by sector, blue is delta from previous year
<?php echo $table->asXML(); ?>

Conclusion of the first part:

Article turned somewhat different than I expected. Having nicer lambda syntax would improve readability so much. Closures are great in limiting local scope, generators are really nice to use, in some other languages they require more syntax. Nikic does wonders for PHP.

We have nicely formatted data, look at this design masterpiece!
data screenshot

 Credits:

UN data

http://data.un.org/
Total employment, by economic activity (Thousands)  [1980-2008]
http://data.un.org/Data.aspx?q=employment&d=LABORSTA&f=tableCode%3a2B

https://github.com/nikic/iter
https://github.com/lstrojny/functional-php

The post Road to the Code with Less Bugs? appeared first on Inchoo.

]]>
http://inchoo.net/magento/programming-magento/less-bugs/feed/ 0
Minify your CSS and JavaScript code! http://inchoo.net/magento/minify-your-css-and-javascript-code/ http://inchoo.net/magento/minify-your-css-and-javascript-code/#comments Tue, 29 Nov 2016 14:37:07 +0000 http://inchoo.net/?p=28074 If you are reading this article, you are probably somehow involved with web development. It is also most likely that you already know how search engines use page speed as one of the parameters for evaluating your site. We have couple of options on our disposal when it comes to increasing site speed. Today we will...

The post Minify your CSS and JavaScript code! appeared first on Inchoo.

]]>
If you are reading this article, you are probably somehow involved with web development. It is also most likely that you already know how search engines use page speed as one of the parameters for evaluating your site. We have couple of options on our disposal when it comes to increasing site speed. Today we will cover one of them.

Probably the simplest method of increasing site speed is minifying CSS and JavaScript. Even though this is pretty simple and straight forward method, for some reason, a lot of Magento stores and generally websites, don’t use this benefit. We have seen different cases where developers completely ignored code minification. Also, we have seen partially minified code. Partially means that, for example, theme code is minified but there is a lot of inline code which isn’t and it is possible to relocate and minify it. We’ve also seen cases where main theme code is minified but code from some other source (extension etc.) isn’t.

Before we go any further let us just ensure that we are on the same page about what “minified code” means. So, minified code is code without:

  • Whitespace characters
  • New line characters
  • Comments
  • Block delimiters.

Let us to suggest few possible options how to minify your code for production.

Minifying CSS and JavaScript code

In order to minify code we first need to decide are we going to do it manually or automatically. We’ll suggest to do automatically in order to speed up the development process, but if for some reason you want to do it manually, we don’t mind.

Do It Manually

CSS Minifying

If you are a developer who doesn’t use any kind of frontend “wizard” on your project (SASS, LESS, Gulp, Grunt etc.), the best option for you will be to use some kind of online or offline minifying tool. For example, one of the online minifying tools you can use is cssminifier.
If you are working on a complex project, you’re probably using some kind of IDE software. In that case you can speed things up by using some kind of IDE plugin that you can install or enable. That will minify the code every time when you save the working stylesheet file.

JavaScript Minifying

Very similar to minifying CSS code. There is bunch of online tools and different kind of IDE extensions for minifying JavaScript code. For example, good online tools are jscompress or javascript-minifier.

Do it automatically

This approach highly depends on the project and what we can or can’t change on it. Sometimes, it’s not possible to change the whole development environment just like that, especially if we’re collaborating with other developers on the same project. It’s not easy to suggest the best possible solution for everyone, but we’ll share what are we using on our Magento projects. We will assume that our audience is familiar with SASS, Compass, Gulp etc. If you need more information, please check out this article from my colleague. You can learn a lot of new things there.

SASS

If you already using SASS on your project, this will be easy for you. Just add additional parameter on sass –watch command:

sass --watch file.scss:file.css --style compressed

But, if you are not familiar with SASS then keep reading, maybe you will find even something better for you.

Compass

If project has Compass included, we already have Compass config file in the project. Just search for config.rb file, open the file and you should see something that looks something like the following:

# Require any additional compass plugins here.
 
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "stylesheets"
sass_dir = "sass"
images_dir = "images"
javascripts_dir = "javascripts"
 
# You can select your preferred output style here (can be overridden via the command line):
output_style = :compressed # :expanded or :nested or :compact or :compressed
 
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true
 
# To disable debugging comments that display the original location of your selectors. Uncomment:
line_comments = false

The config.rb file, Line 11 will likely be commented with the # sign. Uncomment the output_style (remove #). Then restart Compass to start watching for changes in SCSS files. To trigger it enter the command:

$ compass watch

Gulp

Gulp is a truly useful tool for better and faster frontend development. Here on Inchoo blog we have already mentioned few Gulp benefits. We often suggest using Gulp as one of the best tools for complex projects such as Magento frontend development itself.
With Gulp we have wide range of tools at our disposal which can be used to solve different kinds of development problems. But, currently we are only interested in code magnification tasks.

There is couple of ways to handle code magnification in Gulp, but we prefer these two Gulp tasks for that:

Minify CSS and JS code in Magento 1.x

If we take Magento 1.x for example, we need to handle with a lot of inline CSS and Javascript code inside template files. And in order to achieve the best possible results with code magnification, we need to take a look at your Magento template files and find all CSS and JavaScript code that we can transfer to our theme files. Keep in mind that in some cases we will not be in position to transfer all of them to appropriate theme files. Mostly in cases where JavaScript is depending on some php input/output.

Also, we suggest that while we’re transferring JavaScript code to theme file, we double check what code we need on which page. It is possible that we don’t need everything loaded on all Magneto pages. For example, some chunk of code is required just on the product page or just on the home page. That gives us possibility of splitting code in two different files which we can call on different occasions by using Magneto XML engine.

Let us create new gulp tasks which we will using for fast minifying code:

var gulp = require('gulp');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
 
// Css paths
var cssInput = '../sass/**/*.scss';
var cssOutput = '../css';
 
// script paths
var jsSkinInput = '../js/**/*.js';
var jsSkinOutput = '../js/';
 
// Js min task
gulp.task('skin-scripts', function() {
    return gulp.src(jsSkinInput)
        .pipe(uglify())
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(gulp.dest(jsSkinOutput));
});
 
// Sass task
gulp.task('sass', function () {
    return gulp.src(cssInput)
        .pipe(sass({
            errLogToConsole: true,
            outputStyle: 'compressed'
        }))
        .pipe(gulp.dest(cssOutput))
});
 
// Sass file watcher
gulp.task('sass-watch', function() {
    return gulp
        .watch([cssInput, cssOutput], ['sass','browser-reload'])
        .on('change', function(event) {
            console.log('Css File ' + event.path + ' was ' + event.type + ', running tasks...');
        })
});
 
// Js file watcher
gulp.task('skin-scripts-watch', function() {
    return gulp
        .watch([jsSkinInput,jsSkinOutput], ['skin-scripts','browser-reload'])
        .on('change', function(event) {
            console.log('Js File ' + event.path + ' was ' + event.type + ', running tasks...');
        });
});
 
gulp.task('default', ['skin-scripts', 'skin-scripts-watch', 'sass', 'sass-watch']);

As we can see, we are watching for changes on all JavaScript and SASS files in our theme structure. Gulp recognized it and after few seconds Gulp generated minified code at the end. The first two task (skin-scripts and sass) are responsible for CSS and JavaScript minifying, and they work very similar, only difference being that JS files are renamed after minifying process by adding suffix .min at the end of file name. We wish to keep original file for further work and adjustments.

When you are ready, enter this command in your terminal in order to start Gulp tasks:

gulp default

Sounds simple right. But with Magento there’s always something additional 🙂

The thing is that Magento uses JavaScript not just in themes scope, but on the whole system. Major part of JavaScript which is responsible for keeping the system running is located in Magento root (js folder).

As we mentioned before, our goal is to have full minified JavaScript code. And we wish to do things properly. So we have two options:

First one is to adjust our Gulp task so that our task minifies root JavaScript code also, but that isn’t the best possible option. Why? Because with that action we will additionally complicate further maintenance and Magento upgrades. So, in order to avoid that, we want to minify root JavaScript code through Magento backend adjustments. Just before Magento JavaScript code merge.

The second option will be presented by my colleague Tomislav. Instructions for that will come soon.

Keep reading our blog and let us know what you think in the comments below 🙂

The post Minify your CSS and JavaScript code! appeared first on Inchoo.

]]>
http://inchoo.net/magento/minify-your-css-and-javascript-code/feed/ 2
Meet Magento Croatia 2017 – save the dates! http://inchoo.net/magento/meet-magento-croatia-2017/ http://inchoo.net/magento/meet-magento-croatia-2017/#comments Thu, 17 Nov 2016 13:11:03 +0000 http://inchoo.net/?p=28028 Ready for the newest addition to the global family of Meet Magento events? We sure are! Save the dates, 17 – 18 March 2017, ’cause that’s when we’ll be seeing you in Osijek for the first ever Meet Magento Croatia. 2016 was a huge year for Inchooers. After checking off two major things from our...

The post Meet Magento Croatia 2017 – save the dates! appeared first on Inchoo.

]]>
Ready for the newest addition to the global family of Meet Magento events? We sure are! Save the dates, 17 – 18 March 2017, ’cause that’s when we’ll be seeing you in Osijek for the first ever Meet Magento Croatia.

2016 was a huge year for Inchooers.

After checking off two major things from our to do list (putting together Developers Paradise and moving to a new office), we decided 2017 will be no different. To set the tone right, we already started planning some rather exciting new things.

Meet Magento Croatia 2017 is one of those things and we can’t wait to see you there!

Yes, it’s true that Meet Magento events have a tendency to be more oriented towards the local community. We admit that’s the case in our scenario too. But, when local forces are joined by the strong international community, that’s when the magic happens.

So, we are excited to welcome you – members of the extraordinary Magento community to our hometown!

For those of you who’ve been to Developers Paradise and miss it, and for those of you who heard rumours and want to be part of the next conference organized by Inchoo – we suggest you to get your tickets now. 

Number of tickets is limited and we wouldn’t want you to miss the event ’cause you waited too long.

lobby-bar-broj-1

Why join?

Simply put – we’ll do everything that’s in our power to make it amazing. Last time it certainly paid off, we hope it will pay off this time too.

We will have separate Biz and Dev tracks with Dev track entirely in English so if you hope to tackle Magento 2 right on the spot or to share war stories with your old and some new friends – this is the place to be.

Care to share?

Interested in sharing the story of how you tackled a certain Magento 2 challenge with everyone? You can (and should!) apply as a speaker – check out the form here and let us know what you would like to speak about.

All other info (such as the location and Early Bird ticket price) can be found on this link. If there are some uncertainties left, we even got you covered with a nice FAQ.

Hopefully, this sparked your interest and we’ll see you in Osijek soon.

pogled-drava-s-hotela

Interested? Great!

The next thing you’d like to do is to book your ticket. Based on our DevParadise experience, Early Bird has a tendency of flying off really fast once it catches the worm. Now, you’d like to get your ticket before that happens, right? 😉

The post Meet Magento Croatia 2017 – save the dates! appeared first on Inchoo.

]]>
http://inchoo.net/magento/meet-magento-croatia-2017/feed/ 1
Meet Magento World brings Magento experts to your home and office http://inchoo.net/magento/meet-magento-world-2016-online-conference/ http://inchoo.net/magento/meet-magento-world-2016-online-conference/#respond Mon, 07 Nov 2016 11:26:49 +0000 http://inchoo.net/?p=28000 Meet Magento World is a new addition to Magento ecosystem – for the first time ever you’ll be able to attend a Magento conference without leaving your home or your office. On July 11th – 13th 2017 you can take part in this global online event and get direct access to all the top speakers and hottest Magento...

The post Meet Magento World brings Magento experts to your home and office appeared first on Inchoo.

]]>
Meet Magento World is a new addition to Magento ecosystem – for the first time ever you’ll be able to attend a Magento conference without leaving your home or your office.

On July 11th – 13th 2017 you can take part in this global online event and get direct access to all the top speakers and hottest Magento and eCommerce topics at once.

Read on to learn more about this event and hear from one of the top speakers – our own Ivona Namjesnik – what you can expect.

When you’re a part of Magento ecosystem, looking back at a year behind you measure it, among other things, by Meet Magento events you’ve attended – as a delegate, sponsor or a speaker. Perhaps even by the ones you organized.

2016 was packed with these, and it’s only November. So, now is as good time as any to start preparing for something completely different. For the first time ever, in July 2017 there’s going to be a Meet Magento World, taking place in, well – anywhere and everywhere actually!

Over the course of three days (July 11th – 13th) more than 30 speakers will give sessions in the different conference rooms on various Magento and eCommerce topics. The sessions will be held by a long list of international speakers.

Who are the speakers and why them?

You can check out the full agenda for this 3-day event here – the speaker list has been carefully crafted out of Meet Magento veterans and those speakers who have been the best of the best – those people who got highest scores for their speeches at various local Meet Magento events around the world.

And since it’s often very rare to gather the cream of the crop in one place, Meet Magento World is a great opportunity to infuse yourself with the hottest topics presented by great speakers in a packed 3 days of events.

One speaker’s perspective – an interview with Ivona

Now, we are very proud to have one of our own make the cut – Ivona Namjesnik was invited to participate, so here’s her two cents (and some change) about this event.

Ivona, hi. Congratulations for being the voice of Inchoo at Meet Magento World! How does it feel to prepare for the global stage?

Aron, hi. I must admit it’s extremely flattering. Even though I believe I’m quite a “chatterbox”, I never imagined it will be part of my job description. Especially on the global level! I guess learning to channel your natural abilities sure comes in handy and makes your life a whole lot interesting.

So, let’s back up a bit. You got invited to speak at Meet Magento World mostly because of your speech at Meet Magento Czech Republic earlier this year when you got almost the perfect score – what did you talk about in Prague?

Prague was quite an adventure! I absolutely loved it there and I believe overall impression after my topic “No content marketing? No future for your eCommerce business” was really good.

I tried to explained (and if score is to be trusted, I succeeded) the role of content marketing and how it interferes with eCommerce in general. Basically, they are a match made in heaven and talk explained why.

And what about now? How will your presentation look like this time around?

I believe every brand needs a voice. And every brand has one! What brands tend to forget is a fact that being silent sends a message too.

In this talk, I’ll try to reveal the mystery of finding brand voice and following it – both on social media and when approaching customers.

ivona-mmcz-2016

In your interview before MMCZ in Prague you shared some thoughts about number of women (i.e. the lack of) speaking at Magento events, and tech events in general. We can all feel there is a slight shift taking place, but how do you feel this process can be sped up? And should it?

That’s a tough question. As a founder of a Lean In Osijek circle, I believe we as a society should do everything that’s in our power to make sure men and women are treated equally. If speeding up the process means encouraging men and women ever the same and cheering them on, yes, I’m all up for it.

For me, that’s the only normal and natural thing to do and it’s silly to think there are people who would support someone more based on their gender.

What do you think about the concept of Meet Magento World? What are the pros and cons if you compare it to a “regular” Meet Magento event?

I guess it depends on how one thinks of it. I love, and I believe delegates will love too, the fact that you can incorporate it into your working schedule easily. Meet Magento World doesn’t require travelling and you can be in something as comfy as PJ’s.

On the other hand, you miss out on mingling with the community – greatest pro of “3D” Magento events is meeting wonderful people, sharing a cup of joe and exchanging stories and experiences throughout the conference. We’ll have to think of a way to compensate for that. 🙂

Since this event is something really new in the Magento ecosystem, can you put on a fortune teller hat (as opposed to the storyteller one you wear on any given weekday) and tell us how do you see this event play out? Epic or fail?

Hahah, I never really like those hats. But, I see a lot of effort being put into this and amazing colleagues from the Magento community participating – you can only imagine my predictions. 😉

So epic it is. Thanks for your time, Ivona. Any final thoughts to people thinking about becoming a part of Meet Magento World?

I would usually say “See you at the conference” but given the fact it’s all about digital world and connections now – see you on social media.

So there you have it. The thoughts of one of the Meet Magento World speakers.

NOTE: Meet Magento Association has decided to move the conference from December 2016 which was originally planned to July 2017. You can read about the reasons for this change in this article – Meet Magento World moved to July

Stay tuned and check out Meet Magento World official website and join the conversation #MM16WORLD and #MM17WORLD hashtags on Twitter for the latest news – and see you all at Meet Magento World!

The post Meet Magento World brings Magento experts to your home and office appeared first on Inchoo.

]]>
http://inchoo.net/magento/meet-magento-world-2016-online-conference/feed/ 0
Your Magento shop deserves a custom tailored theme! http://inchoo.net/magento/custom-magento-theme/ http://inchoo.net/magento/custom-magento-theme/#comments Fri, 28 Oct 2016 10:40:18 +0000 http://inchoo.net/?p=27953 Getting your store up is a process. One which starts with a revelation that, if you want to make things work in the long run, you’ll need a webshop. An online place for reaching new and informing existing customers. For them, it’s a brand new channel where they can buy your products. For you, it’s...

The post Your Magento shop deserves a custom tailored theme! appeared first on Inchoo.

]]>
Getting your store up is a process. One which starts with a revelation that, if you want to make things work in the long run, you’ll need a webshop. An online place for reaching new and informing existing customers. For them, it’s a brand new channel where they can buy your products. For you, it’s a strategic decision. Why? Because the idea behind that action is growing your business.

But, apart from choosing the platform or the hosting, you should also think about the visual appeal of your store. In this interview, Marko Briševac, one of Inchoo designers, answers the most frequent questions we get from prospective clients. Read more to find out what’s it all about!

Most of our clients contemplate getting an existing theme template rather than deciding on investing in getting one which is custom made. Why do you think it is extremely important to invest in a theme tailored according to their needs?

Marko: I must admit we get this question a lot. After talking with a lot of clients and fellow designers throughout the years, here’s the best possible answer based on my experience. 

Design process is a series of steps through which designers come up with a solution to a problem. Even though clients think it would be nice to skip all (or some of) those steps and buy a nice looking theme, every step must be completed to ensure a successful project. Design should promote improving the quality of the user’s interaction with a store and perception of a brand. None of these can be achieved with a pre-built theme.

Pre-built themes rarely care for usability, performance or user experience. Authors of these themes are aware of the fact that people are naturally more attracted to the things which are aesthetically pleasing, especially if they come with a whole list of features. However, that doesn’t necessarily mean they are solving a problem for the brand, communicating with customers in the right way or ensuring amazing user experience.

Pre-built theme could work if you are a small business owner with limited funds looking for a simple website as a space to provide general information about your company, but it should not be an option for a thriving eCommerce business.

design-sketching

How does “I want a site that looks exactly like example.com” fit into the picture? Or, better yet, why it doesn’t fit into it?

Marko: There are two different types of clients when it comes to this.

Some clients refer only to visual direction when they mention they like a certain site. For example, 70% of all clients will say they like Apple web site. Solution, in this case, isn’t to copy everything from Apple, but to recognise that the client likes effective use of white space, minimalistic colour scheme, large high quality images, etc. It is necessary to perceive are there some obvious elements on example sites which client likes but doesn’t know how to describe.

Other clients want to copy literally everything – style, features, behaviour, functionalities… I have to say it’s completely understandable why they want to copy from more successful competitors. They simply believe they will achieve the same results. For them, successful competitors’ website is a pattern of how things should be done in their field.

If that’s the case, it is crucial to explain that their business and brand is unique in every way, and what works for one doesn’t for the other even though they may be in the same industry.

We’d like to point out that our job is to carefully consider both the needs of the client and the needs of the user so we can maximize the results. We can achieve that only by planning, research and testing, definitely not by copying from others.

design-theme

Design process is what people usually connect with something being “pretty” when, in fact, it requires a lot of hard work. Can you describe, briefly, what goes into making a custom theme and benefits it yields?

Marko: At Inchoo, our design process consists of several phases through which we emphasise, define, ideate, prototype and test.

Here I’ll just mention that during that process we research, report detected problems and suggest improvements. Our design process focuses on users and their needs and business goals of the client in order to create cohesion, optimize conversion rates and find sustainable growth.

If you’re interested in all of our “behind the scenes” work, you can check out this article which covers every step we go through while working on your custom theme.

Main conclusion is definitely that design today should be (and at Inchoo is) way more than creating a visually pleasing site.

As Marko already pointed out, design is way more than something being pretty. If you’re thinking on whether you should invest in a custom tailored theme for your Magento shop, keep in mind that your customers judge you based on it. The more you understand them and display that through your shop, the more they’re likely to understand – and buy – from you.

The post Your Magento shop deserves a custom tailored theme! appeared first on Inchoo.

]]>
http://inchoo.net/magento/custom-magento-theme/feed/ 3
Enter HTTPS http://inchoo.net/magento/magento-https/ http://inchoo.net/magento/magento-https/#comments Tue, 18 Oct 2016 12:07:23 +0000 http://inchoo.net/?p=27888 In their effort to improve security on the web, Google has been, for some time now, steadily pushing their “HTTPS everywhere” initiative. Besides providing better security for end user by encrypting all traffic between server and browser, HTTPS will most likely be used as a ranking signal. Another interesting fact is that starting from January...

The post Enter HTTPS appeared first on Inchoo.

]]>
In their effort to improve security on the web, Google has been, for some time now, steadily pushing their “HTTPS everywhere” initiative. Besides providing better security for end user by encrypting all traffic between server and browser, HTTPS will most likely be used as a ranking signal. Another interesting fact is that starting from January 2017, Chrome will use icon in the address bar to mark HTTP pages that collect passwords or credit cards as non-secure, as a part of the long-term plan to mark all HTTP sites as non-secure. We can assume that other browsers will adapt this behaviour in the future.

In this blog post, we will cover basic configuration required to run your Magento shop over HTTPS and we will look at some common pitfalls and gotchas.

HTTPS everywhere

Configuring site-wide HTTPS in Magento is easy enough. Assuming that your server is configured properly and that you have a valid SSL certificate, all you have to do is change a few configuration parameters. In System > Configuration > Web update your Unsecure Base URL to start with https, e.g. https://www.example.com/. Additionally, under Secure tab, set both Use Secure URLs in Frontend and Use Secure URLs in Admin to Yes.

This covers the basic configuration required to enable site-wide HTTPS but, as usual, things are never so simple. Let’s take a look at some common issues that can happen once site-wide HTTPS is enabled and how to resolve them.

Redirects

Depending on your configuration, you might face an issue where all your old HTTP links are being redirected to HTTPS homepage.

Example:

http://www.example.com/some-product.html redirects to https://www.example.com/ which is not correct. Expected result is redirect to https://www.example.com/some-product.html

To prevent redirecting to home page, set Auto-redirect to Base URL to No. This option is also located in System > Configuration > Web section. Disabling Auto-redirect, however, introduces another problem. Now both www and non-www variations of your site will be available. This can be fixed with redirect rule on your web server.

RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ https://www.example.com%{REQUEST_URI} [NE,L,R=301]

The example give is for apache web server. What it does is force both HTTPS and www for all incoming requests.

Mixed content

Another common issue is when static content, most commonly images, css and javascript, are hardcoded to load over http protocol. This will cause your browser to issue mixed content / unsecure page warning. To fix this, you will have to review your templates, layouts, static block, cms pages, etc. and update any static content to load over HTTPS. Alternatively you can also use protocol-relative URLs.

For example: //www.example.com/skin/frontend/rwd/default/images/media/logo.png

Magento EE

If you are planning to run site-wide HTTPS and are using Magento EE, make sure you are at least on version 1.14.1 or later, since in prior versions, full page cache was disabled when on HTTPS.

HTTPS on CDN

Serving static content via CDN over HTTPS may result in additional expense, in case you have to purchase additional SSL certificate(s) for your CDN subdomains. Your CDN provider may also charge you for installing SSL certificate. There are also some free options available which vary depending on the CDN provider, but none of them are ideal. Something to keep in mind when adopting site-wide HTTPS.

And with this we will conclude todays blog post. If you already adopted site-wide HTTPS and faced issues similar to this ones or some not mentioned  in this blog post, let us know in the comments section.

Happy browsing and may the SSL be with you.

The post Enter HTTPS appeared first on Inchoo.

]]>
http://inchoo.net/magento/magento-https/feed/ 1
How to keep your CMS blocks organized (and not go insane) http://inchoo.net/magento/organizing-cms-blocks/ http://inchoo.net/magento/organizing-cms-blocks/#comments Thu, 06 Oct 2016 11:37:47 +0000 http://inchoo.net/?p=27799 Head on over to Magento dashboard in your store and take a look at CMS blocks. How many CMS blocks have you got? Can you tell which of the blocks are being used on site and which are currently not active or can be safely removed? Can you pick a random CMS block and instantly...

The post How to keep your CMS blocks organized (and not go insane) appeared first on Inchoo.

]]>
Head on over to Magento dashboard in your store and take a look at CMS blocks. How many CMS blocks have you got? Can you tell which of the blocks are being used on site and which are currently not active or can be safely removed? Can you pick a random CMS block and instantly know what it is and where to find it on site? In this article we’re going to cover a powerful way of organizing, searching and filtering CMS blocks by simply naming them in a specific way. And the best part is that it works out of the box in vanilla Magento without any extensions.

CMS blocks are very useful part of Magento. But there is a chance that, over time, you could have dozens (or even hundreds) of various CMS blocks on your Magento store. Those CMS blocks can be named by different people with different mindsets who use different naming conventions and terminology which, in different cases, can cause confusion and lot of time being lost on finding the block you’re looking for.

Currently, Magento admin dashboard doesn’t provide any effective way of organizing them besides some basic search and filter options. But we can make most of these options and come up with a simple, but powerful way of organizing and keeping track of the CMS blocks that works in vanilla Magento and doesn’t depend on extensions.

Simplest and most obvious solution can be found in naming the blocks in a structured way that makes sense to developers and merchants as well.

Rules, Goals and Basic Structure

Let us establish some rules and goals for our naming structure. Suggested naming convention for Magento CMS block titles must meet the following requirements (in no particular order):

  • It must be universal and applicable for all CMS blocks
  • Each block has to have a unique title
  • Users must be able to filter the blocks effectively and always find what they’re looking for without resorting to blind guessing or the other timewasting methods
  • It must work on vanilla Magento (CE or EE) and it must not dependent on any extension
  • Users should immediately get an idea what is the block’s position and purpose

Taking inspiration from various popular naming conventions and following the requirements, I have come up with the following naming structure for CMS block titles.

[TAG] Website Name – Store View – Template Reference – Layout Position – Component Name – Component Variation

Underlined components represent required parameters when naming a CMS block, rest of the parameters are optional.

Please note that you may use any format that you like and feel like it better fits your project. The presented structure is just a proposed naming structure that made most sense to me at the time.

Before we move onto examining each part of naming structure separately, here is an example of CMS block title following the naming rule and using only the required (underlined) parts:

Global – Footer – Social Media Links

Taking a Closer Look at Each Component

[TAG] (optional)

It can be used to tag specific blocks for a specific reason. Tags should be used as a temporary part of a name until requested action has taken place or the tag becomes outdated after new code deployment.

Examples:

Value Description
[no-value] CMS block isn’t tagged
DELETE Marked for deletion and waiting for the final approval.
EDIT Requested an edit (content, code or design).
BUG-ticket number This CMS block contains an issue or a bug detailed in a ticket or a task defined in the ticket number which is optional.
OUTDATED-id Paired with the id of a block with a tag [NEW], it marks new CMS block which has been replaced with the newer version of the CMS block (with major changes) in recent deployment, but has not been deleted in case of code revert. It can be deleted after the deployment has been verified.
NEW-id Paired with the id of a block with a [OUTDATED] tag, it marks the new version of the CMS blocks that will replace the outdated one when the new code has been deployed to the site.

 

Website Name (optional)

Refers to the website name in Magento structure where the CMS block can be found.

Examples:

Value Description
[no-value] Only one Website
multi Block can be found in multiple websites
[Store Name] Block can be found only in a specific website

 

Store View Name (optional)

Refers to the store view in Magento structure where the CMS block can be found. It can also refer to the i18n internalization to mark identical blocks in different languages.

Examples:

Value Description
[no-value] Website doesn’t have multiple store views
multi Block can be found in multiple store views (Multilanguage)
en English language
de German language

 

Template Reference (required)

Refers to the page name.

Examples:

Value Description
Global Global scope, component appears on all pages
Multi Component appears or 2 or more pages, but not all pages
Category Page Magento Category Page
Product Page Magento Product Page
Homepage CMS Homepage CMS Page
About Us CMS About Us CMS Page

 

Layout Position (required)

Position in the Magento page layout.

Examples:

Value Description
Multi Component appears on multiple containers on a page
Content Main contents, main container
Sidebar Left Left sidebar container
Sidebar Right Right sidebar container
Header Website header
Footer Website footer

 

Component name (required)

Custom name for the CMS block that describes CMS block’s role.

Examples:

Component Name Value
Sale Banner
Size Guide
Category Description

 

Component variation (required only under certain condition)

Variation for the existing CMS block. Required if the CMS block is a different version or an iteration of an existing block.

Examples:

Component Name Value Component Variation Values Description
Component Name [no-value] or Regular No variation, base component
Sale Banner Summer, Holiday, Black Friday Various sale banners
Size Guide Shoes, Shirts, Pants Size Guide for various clothing
Category Description Accessories, Bags, Belts Category page CMS blocks

For the block identifiers, we have to take a different, but simpler approach in naming them. We don’t want to use the same naming format for block identifiers as for the block title, since the idea of the identifier that it doesn’t change to avoid completely refactor the code. I would suggest using the following format for the identifiers to keep up with the structured name format:

component_name-component_variation

Proof of Concept and Examples

Using the presented naming format, let’s convert some block titles for some default Magento 1 CMS Blocks!

Default Magento 1 block title New block title
Footer Links Company Global – Footer – Company Links
Footer Links SM Global – Footer – Social Media Links
Category Landing Page Home Category Page – Content – Home
Category Landing Page Main Block Category Page – Content – Main Block
Cookie Restriction Notice Global – Header – Cookie Restriction Notice

 

New block title New Block Identifier
Global – Footer – Company Links company_links-regular
Global – Footer – Social Media Links social_media_links-regular
Category Page – Content – Home home-regular
Category Page – Content – Main Block main_block-regular
Global – Header – Cookie Restriction Notice cookie_restriction_notice-regular

That looks way better, doesn’t it? Look at all that info that you could get just looking at the CMS block title! We immediately know which store the block’s location on a website, it’s position on a page, content and role just by simply looking at the block title. Now, isn’t that awesome? But true power and purpose of this naming structure is in filtering the blocks.

Filtering CMS blocks by Block Title

Now that we created new naming format for our CMS blocks and gave them more meaningful titles, let’s see how this naming convention fares when searching for a specific block or a specific group of blocks.

It’s important to note the differences in Searching by Block Title in Magento 1 and Magento 2. Whereas Magento 1 is really strict in regards to search words where you have to type exact part of block title, in Magento 2 we have a more lenient search where every word separated by the empty space or every set of words in quote marks separated by the empty space would be searched.

For testing purposes, I have created 20 empty CMS blocks in Magento 1 and Magento 2 (shown in the images) and I gave them random block titles I could think of following the suggested naming format and I gave them their respective block identifiers following the suggested naming format.

cms-01

Example – Scenario 1

Routine inspection for tagged CMS blocks (listing all tagged CMS blocks – bugs, delete requests, edit requests, etc.)

Even though there isn’t a keyword that applies for all CMS blocks which are, we cannot filter all the tagged blocks by searching (even though we can search for an individual tag like “[DELETE]”, for example). Instead of searching for each individual tag, we can make the tagged blocks appear on top of the list by sorting the CMS block title in an ascending order to make the tagged blocks appear first in the CMS block listing.

cms-02

Example – Scenario 2

Listing all CMS blocks that may appear on a specific page (Product page, for example)

Let’s assume that we’re looking for a block that we saw on the Product Page. Sounds simple enough. We just type in “Product Page”, right?

cms-03

But what if the block is “Global” or “Multi”? We should search for the block using page-specific parameters and widen the search to “Multi” or “Global” blocks if we don’t find it using page-specific parameters. We can also combine this search with info about layout position “Content” or “Sidebar Left” for example, to get better results while filtering. All this could be done using vanilla Magento filtering feature available in CMS Block Admin page. We can also use improved filtering in Magento 2 to get all results in one search.

cms-04

And finally we end up with results we’re looking for. This way we get a list of all possible blocks that appear on a Product Page (excluding blocks marked with “Multi” which may or may not appear on the Product Page).

Same approach applies when searching the CMS blocks for the CMS block in the specific store, in the specific store view (language) and in the specific section in a page layout.

Example – Scenario 3

Listing all CMS blocks with specific variation (listing all holiday CMS blocks, for example).

It is important to also have rule when naming CMS block variations to allow easier filtering. In this case, if we have included a variation name “Holiday” in all of the CMS block holiday variations, we would simply have to search the CMS blocks by the keyword “Holiday” to display only the holiday CMS blocks.

Example – Scenario 4

Deploying a revamped page element (footer, for example) with updated CMS block (social media links, for example).

Before any code deployment, we need to move our updated CMS block for footer social media links and give it a tag [NEW-id] with the id of the original social media links block. After the code deployment and after verifying the deployment success, we need to update the original social media links block title with tag [OUTDATED-id] with the id of the new block. After a few days and if code revert isn’t needed and the block isn’t going to be used anymore, it is safe to either give it a [DELETE] tag or delete it.

Answers to Some Questions That You Might Have

Come on, is it even possible to have that many CMS blocks in your project?

Yes! From personal experience, I worked on projects who had the number of CMS blocks ranging from few dozen CMS blocks on a smaller project, to about a hundred blocks on a regular project and finally, to more than a thousand CMS blocks on a long-term large-scale project.

Why have “Global” and “Multi” values for Template Reference? What is the point?

“Global” refers to the elements that are defined as default in the Magento layout and they aren’t removed for any page/s on the site. “Multi” refers to the elements that are defined as default in the Magento layout, but which are removed from at least one page in the Magento layout.

Practical example would be removing excessive (and distracting) header elements only on checkout page to make sure customer is completely focused on the checkout form. In that case, you would need to use “Multi” in the removed CMS block’s name.

Why not just get an extension that fixes this issue?

Why not, indeed. Go for it. This is conceived as a free alternative that works out of the box and uses vanilla Magento features. All it takes is coordination and established rule between people who create and manage CMS blocks.

Editing that many block titles can become tedious when refactoring!

Magento 2 allows making quick changes to the CMS blocks (like changing a block title) right from the CMS block listing. Even though this method requires opening the CMS block in Magento 1 which is an extra step in comparison to Magento 2, the benefits greatly outweigh those occasional inconveniences.

cms-05

I have an idea for a better naming structure!

Let me know in the comments. I am curious what other developers will come up with and how they organize and prioritize CMS block info.

Conclusion

Working with CMS blocks can be a messy job and organizing them can be even messier. It is astounding how much better we can manage CMS blocks simply by establishing naming rules. I hope that you found this method of naming CMS blocks useful and that it will help you in your projects. I am also hoping that this is just a temporary solution and that folks over at Magento will come up with a better way of organizing and keeping track of CMS blocks and CMS pages.

What do you think about this idea? Can you see yourself using an established naming rules for CMS blocks in your current or future projects? Or you have a better suggestion for the naming rules? I am curious to know your thoughts, opinions and ideas, so feel free to leave me a comment below.

The post How to keep your CMS blocks organized (and not go insane) appeared first on Inchoo.

]]>
http://inchoo.net/magento/organizing-cms-blocks/feed/ 8
Mitigating Facebook’s “x-fb-http-engine: Liger” site hammering using Apache or nginx http://inchoo.net/dev-talk/mitigating-facebook-x-fb-http-engine-liger/ http://inchoo.net/dev-talk/mitigating-facebook-x-fb-http-engine-liger/#comments Tue, 04 Oct 2016 13:51:19 +0000 http://inchoo.net/?p=27784 Facebook is using a sort of web page prerendering mechanism in it’s in-app browser for mobile users. It is used by Facebook’s in-app browser to speed up page rendering for web link content shared on Facebook. But it can cause serious headaches, and this post should explain how to mitigate it with Apache or nginx.

The post Mitigating Facebook’s “x-fb-http-engine: Liger” site hammering using Apache or nginx appeared first on Inchoo.

]]>
Facebook is using a sort of web page prerendering mechanism in it’s in-app browser for mobile users [see disclosure here]. It is used by Facebook’s in-app browser to speed up page rendering for web link content shared on Facebook. But it can cause serious headaches, and this post should explain how to mitigate it with Apache or nginx.

Web page prerendering is not uncommon way of reducing frontend load times. Most today’s browsers support it, and some of them use it regularly (like Chrome – did you notice how first results on Google sometimes render instantly?!).  Some browsers prefer “preview” instead of “prerender” [like Safari], so other browsers [like Chrome] had to accept both instructions for the same action.

We all know that Magento CPU cycles come at a price. You should test on your own whether this FB in-app feature has implications on your web server performance and expenditure.

Facebook’s “Liger preview” requests can be recognized in your server logs by:

  • HTTP referer: “http://m.facebook.com
  • User-Agent has: FB_IAB/FB4A;FBAV (Facebook in-app browser for Android)
  • x-fb-http-engine: Liger – this is the identifier of the engine, BUT, FB in-app does not send it every time! It is being sent only when X-Purpose is sent:
  • X-Purpose: preview – page prerender/prefetcher HTTP header
  • regular images are not loaded – FB in-app requests only for HTML page

Every owner of Facebook page(s) that regularly shares links and with more than 10K fans should test their website for this particular page requests, as it might severely impact web site performance. Please, check this article on how to precisely log and test how severe your web site is affected.

I’m affected, what now?!

Since these requests are marked with two significant HTTP headers, we’ll use them to block this type of requests. We don’t want to block ALL prerender/preview requests, only those that come from Facebook’s in-app browser engine signed as “Liger“.

In Apache, you can use [tested, verified]:

RewriteCond %{HTTP:x-fb-http-engine} Liger
RewriteCond %{HTTP:X-Purpose} preview
RewriteRule .* - [F,L]

 

In nginx, you can use [not tested, but should work; please verify]:

This rule checks for presence of one HTTP header [faster]:

if ($http_x_fb_http_engine = "Liger") {
return 403 "Access denied due to Facebook's 'Liger preview' site hammering";
}

This one covers requests when both headers are present [since nginx does not support logical AND, we have to use a trick] :

if ($http_x_fb_http_engine = "Liger") {
set $lp L;
}
if ($http_x_purpose = "preview") {
set $lp "${lp}P";
}
if ($lp = "LP") {
return 403 "Access denied due to Facebook's 'Liger preview' site hammering";
}

And that’s it!

Don’t forget, prerendering can be quite cool and useful, so not all prerender/preview requests should be blocked – only those that originate from “Liger” engine. Prerendering advantages will be covered in the next article so – stay tuned!

The post Mitigating Facebook’s “x-fb-http-engine: Liger” site hammering using Apache or nginx appeared first on Inchoo.

]]>
http://inchoo.net/dev-talk/mitigating-facebook-x-fb-http-engine-liger/feed/ 1
Magento 2: How to add new search engine like Solr or Elasticsearch http://inchoo.net/magento-2/magento-2-add-solr-elasticsearch/ http://inchoo.net/magento-2/magento-2-add-solr-elasticsearch/#comments Tue, 27 Sep 2016 11:41:20 +0000 http://inchoo.net/?p=27740 Magento 2 Community Edition comes with support for only MySQL search engine, but some projects require better or more adjustable search engine in order to increase sales or conversion rate. In this situation we are implementing Solr or Elasticsearch search engine. In this post we will create a skeleton code or rough example which will...

The post Magento 2: How to add new search engine like Solr or Elasticsearch appeared first on Inchoo.

]]>
Magento 2 Community Edition comes with support for only MySQL search engine, but some projects require better or more adjustable search engine in order to increase sales or conversion rate.
In this situation we are implementing Solr or Elasticsearch search engine.

In this post we will create a skeleton code or rough example which will introduce main classes and methods by which we can implement additional search engine like Solr or Elasticsearch. If you take a look in Magento 2 admin you can find search engine configuration on next location: Stores -> Configuration -> Catalog -> Catalog Search and drop down “Search Engine”.

In drop-down list you will notice that you have only MySQL engine and our first step will be to add additonal option in this drop-down list with label “Solr”. So, let’s start.

As per usual, you need to create a Magento 2 module (I suppose that you already know this process but if you don’t, you can read the tutorial here). In your module in etc folder you need to create file di.xml with next xml code:

    <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
        <arguments>
            <argument name="engines" xsi:type="array">
                <item name="solr" xsi:type="string">Solr</item>
            </argument>
        </arguments>
    </type>

With this xml code we added a new option to our drop-down list with the option name “Solr“. If you created it properly and cleaned Magento cache, you will be able to see it in the drop-down where there’ll be a new option “Solr”. If you see it then it means that you added it properly.

In the next step we will start with php classes which is in charge for indexing data to search server.

First of all, we should implement Engine class, put in di.xml next code:

    <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider">
        <arguments>
            <argument name="engines" xsi:type="array">
                <item name="solr" xsi:type="string">Inchoo\Solr\Model\ResourceModel\Engine</item>
            </argument>
        </arguments>
    </type> 

You can see that we introduced our own Engine class for “Inchoo\Solr\Model\ResourceModel\Engine“. Engine class is in charge for preparing data before it goes to our indexerHandler class (last endpoint before solr server) and Engine class has to implement: \Magento\CatalogSearch\Model\ResourceModel\EngineInterface.

Interface class contains next four methods:
processAttributeValue prepare attribute value to store in solr index
getAllowedVisibility retrieve allowed visibility values for current engine
allowAdvancedIndex define if current search engine supports advanced index
prepareEntityIndex prepare index array as a string glued by separator

These methods are mandatory and have to be implemented in your Engine class. For better understanding you can check/compare logic in similar MySQL native class: Magento\CatalogSearch\Model\ResourceModel\Engine.

Our example of skeleton class is below:

<?php
namespace Inchoo\Solr\Model\ResourceModel;
 
use Magento\CatalogSearch\Model\ResourceModel\EngineInterface;
 
 
class Engine implements EngineInterface
{
 
    protected $catalogProductVisibility;
    private $indexScopeResolver;
 
    public function __construct(
        \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility,
        \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver $indexScopeResolver
    ) {
        $this->catalogProductVisibility = $catalogProductVisibility;
        $this->indexScopeResolver = $indexScopeResolver;
    }
 
    public function getAllowedVisibility()
    {
        return $this->catalogProductVisibility->getVisibleInSiteIds();
    }
 
    public function allowAdvancedIndex()
    {
        return false;
    }
 
    public function processAttributeValue($attribute, $value)
    {
        return $value;
    }
 
    public function prepareEntityIndex($index, $separator = ' ')
    {
        return $index;
    }
 
    public function isAvailable()
    {
        return true;
    }
}

Next step is creating indexerHandler with name “Inchoo\Solr\Model\Indexer\IndexerHandler” which has to implement Magento\Framework\Indexer\SaveHandler\IndexerInterface.
For implemetation of IndexerHandler you should add next code in your di.xml file:

   <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory">
        <arguments>
            <argument name="handlers" xsi:type="array">
                <item name="solr" xsi:type="string">Inchoo\Solr\Model\Indexer\IndexerHandler</item>
            </argument>
        </arguments>
    </type>

If you open IndexerInterface you will see four methods which you have to implement:
saveIndex add entities data to index
deleteIndex remove entities data from index
cleanIndex remove all data from index
isAvailable define if engine is available (you can implement ping to solr server and check is it live).

Our example of IndexerHandler skeleton class is below:

<?php
namespace Inchoo\Solr\Model\Indexer;
 
use Magento\Eav\Model\Config;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Indexer\SaveHandler\IndexerInterface;
use Magento\Framework\Indexer\IndexStructureInterface;
use Magento\Framework\Search\Request\Dimension;
use Magento\Framework\Search\Request\IndexScopeResolverInterface;
use Magento\Framework\Indexer\SaveHandler\Batch;
use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
 
class IndexerHandler implements IndexerInterface
{
    private $indexStructure;
 
    private $data;
 
    private $fields;
 
    private $resource;
 
    private $batch;
 
    private $eavConfig;
 
    private $batchSize;
 
    private $indexScopeResolver;
 
    public function __construct(
        Batch $batch,
        array $data,
        $batchSize = 50
    ) {
        $this->batch = $batch;
        $this->data = $data;
        $this->batchSize = $batchSize;
    }
 
    public function saveIndex($dimensions, \Traversable $documents)
    {
        foreach ($this->batch->getItems($documents, $this->batchSize) as $batchDocuments) {
 
        }
    }
 
    public function deleteIndex($dimensions, \Traversable $documents)
    {
        foreach ($this->batch->getItems($documents, $this->batchSize) as $batchDocuments) {
 
        }
    }
 
    public function cleanIndex($dimensions)
    {
 
    }
 
    public function isAvailable()
    {
        return true;
    }
}

In these methods you should implement Solr PHP client which will proceed listed operations to Solr server. Very often used is Solarium PHP client.

With this step we are ending with process of indexing data to search-server.

Now you can check are your indexer works with next command (before in set search engine to SOLR in magento admin):

php /bin/magento indexer:reindex catalogsearch_fulltext

In the next, last step we will explain how to implement a new search engine on the Magento 2 frontend. Also, we have to modify di.xml and add next code:

<type name="Magento\Search\Model\AdapterFactory">
        <arguments>
            <argument name="adapters" xsi:type="array">
                <item name="solr" xsi:type="string">Inchoo\Solr\SearchAdapter\Adapter</item>
            </argument>
        </arguments>
    </type>

Our new adapter is class Inchoo\Solr\SearchAdapter\Adapter. Adapter class should implement Magento\Framework\Search\AdapterInterface. In our adapter we have to implement method query – this method accepts query request and process it. Take a look of our example and everything will be more clear.

<?php
namespace Inchoo\Solr\SearchAdapter;
 
use Magento\Framework\Search\AdapterInterface;
use Magento\Framework\Search\RequestInterface;
use Magento\Framework\Search\Response\QueryResponse;
use Inchoo\Solr\SearchAdapter\Aggregation\Builder;
 
 
class Adapter implements AdapterInterface
{
    protected $responseFactory;
 
    protected $connectionManager;
 
    protected $aggregationBuilder;
 
    public function __construct(
        ResponseFactory $responseFactory,
        Builder $aggregationBuilder,
        ConnectionManager $connectionManager
    ) {
        $this->responseFactory = $responseFactory;
        $this->aggregationBuilder = $aggregationBuilder;
        $this->connectionManager = $connectionManager;
 
    }
 
    /**
     * @param RequestInterface $request
     * @return QueryResponse
     */
    public function query(RequestInterface $request)
    {
        $client = $this->getConnection();
        $documents = [];
 
        $documents[1007] = array('entity_id'=>'1007', 'score'=>46.055);
        $documents[1031] = array('entity_id'=>'1031', 'score'=>45.055);
        $documents[1120] = array('entity_id'=>'1120', 'score'=>44.055);
 
        $aggregations = $this->aggregationBuilder->build($request, $documents);
 
        $response = [
            'documents' => $documents,
            'aggregations' => $aggregations,
        ];
        return $this->responseFactory->create($response);
    }
 
    public function getConnection(){
        return $this->connectionManager->getConnection();
    }
}

In our demo adapter class we hard coded product entity_ids: 1007, 1031, 1120 from our database product-ids, only for testing purpose. If you want to dig deeper I suggest that you examine logic how MySQL native adapter works.

With this step we are ending our example. Even though things seem pretty complicated, when you start working, everything will be fine. I hope that you will enjoy the coding of your new search engine for Magneto 2.

The post Magento 2: How to add new search engine like Solr or Elasticsearch appeared first on Inchoo.

]]>
http://inchoo.net/magento-2/magento-2-add-solr-elasticsearch/feed/ 2
Custom API for Magento 2 http://inchoo.net/magento/api-magento/magento-2-custom-api/ http://inchoo.net/magento/api-magento/magento-2-custom-api/#comments Thu, 08 Sep 2016 12:40:08 +0000 http://inchoo.net/?p=27625 We have already went through on how to configure integration and utilize Magento apis. But let’s see how to make our own module with custom API calls. Module Essentials For a simpler start you should read the following article: “How to create a basic module in Magento 2” by Hrvoje Ivancic and make basic Magento 2 module....

The post Custom API for Magento 2 appeared first on Inchoo.

]]>
We have already went through on how to configure integration and utilize Magento apis. But let’s see how to make our own module with custom API calls.

Module Essentials

For a simpler start you should read the following article: “How to create a basic module in Magento 2” by Hrvoje Ivancic and make basic Magento 2 module. You would need only two things, module.xml and register.php for this example module. I would create module under Inchoo name space and call it Hello.

Module Configuration – 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="Inchoo_Hello" setup_version="1.0.0" />
</config>

Registration – registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Inchoo_Hello',
    __DIR__
);

API configuration

There are two more additional configurations we need to add API capability to module, webapi.xml and di.xml. In webapi.xml we are configuring access rights and API Interface that specified method will use.

Web API configuration – etc/webapi.xml

<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route url="/V1/hello/name/:name" method="GET">
        <service class="Inchoo\Hello\Api\HelloInterface" method="name"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

Resource tag defines what resources user needs to have to be able to access this api call. Possible options are self, anonymous or Magento resource like Magento_Catalog::products or Magento_Customer::group. We will for now use anonymous so we can access it as a guest.

Define Interface – etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Inchoo\Hello\Api\HelloInterface"
                type="Inchoo\Hello\Model\Hello" />
</config>

In di.xml we define what model will interface call to. We also need to add Interface and Model, please note that you need to take care about comments also.

Interface – Api/HelloInterface.php

<?php
namespace Inchoo\Hello\Api;
 
interface HelloInterface
{
    /**
     * Returns greeting message to user
     *
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
     */
    public function name($name);
}

 

Model – Model/Hello.php

<?php
namespace Inchoo\Hello\Model;
use Inchoo\Hello\Api\HelloInterface;
 
class Hello implements HelloInterface
{
    /**
     * Returns greeting message to user
     *
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
     */
    public function name($name) {
        return "Hello, " . $name;
    }
}

Within model we add our functionality that will be executed by call to API method. In this case it will append Hello to name provided by call and return as string.

With all this your module should look like this:

file_tree

 

Communicating with new API call

Testing as guest

To test REST you can go to http://{domain_name}/rest/V1/{method}/{attribute}/{value}.

Example: http://magento2.loc/rest/V1/hello/name/Jim

This is how response should look like for this example:

<response>Hello, Jim</response>

Here is small code that will test same API call but with SOAP:

<?php
$proxy = new SoapClient('http://magento2.vm/index.php/soap/default?wsdl&services=inchooHelloV1');
$result = $proxy->inchooHelloV1Name(array("name"=>"Jim"));
var_dump($result);

Response for SOAP

object(stdClass)#2 (1) {
  ["result"]=>
  string(10) "Hello, Jim"
}

Adding ACL

If we don’t set anonymous in resource of webapi.xml, we need to set existing Magento resource or create our own. We can do that by adding acl.xml to etc.

ACL – etc/acl.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Inchoo_Hello::hello" title="Hello" translate="title" sortOrder="110" />
            </resource>
        </resources>
    </acl>
</config>

 

In this case we need to add “Inchoo_Hello::hello” to webapi.xml resource instead anonymous.

In article Magento 2 API usage with examples by Tomas Novoselic is covered how we can connect to Magento with REST or SOAP API and we can use the same example to create new integration and test new implementation of API call.

Since Magneto 2 is still fresh this may change in time, but we will try to keep this up to date with latest version. This is tested with Magento v2.1.0.

 

The post Custom API for Magento 2 appeared first on Inchoo.

]]>
http://inchoo.net/magento/api-magento/magento-2-custom-api/feed/ 4
Is your website being hammered by Facebook and what liger has to do with it? http://inchoo.net/dev-talk/magento-website-hammering-facebook-liger/ http://inchoo.net/dev-talk/magento-website-hammering-facebook-liger/#comments Mon, 29 Aug 2016 12:19:26 +0000 http://inchoo.net/?p=27562 Do you or your client have popular Facebook fanpage?! If so - this article might be interesting to you, but first - let’s start with definition of “hammering”, although the term is not unknown to webmasters…

The post Is your website being hammered by Facebook and what liger has to do with it? appeared first on Inchoo.

]]>
Do you or your client have a popular Facebook fanpage?! If so – this article might be interesting to you, but first – let’s start with definition of “hammering”, although the term is not unknown to webmasters…

define: hammering:

  • gerund or present participle: hammering
    hit or beat (something) repeatedly with a hammer or similar object.
  • strike or knock at or on something violently with one’s hand or with a hammer or other object.
  • work hard and persistently
    “they must hammer away at these twin themes day after day”
  • inculcate something forcefully or repeatedly.

Hard and persistently.

What’s website hammering and how to spot it?

Website hammering, originally derived from (“hard and persistent”) process of trying to connect to FTP servers in short period of time, nowadays includes all sorts of services, and can cause all sorts of availability and performance issues. In this particular case, it would be getting “hard and persistent” page requests.

Do you check your web logs from time to time?! I know, none us do as often as we should, as browsing through them can be a mix of painful experience and eye-bleeding match-a-pattern game.

In one of those “games” I noticed something interesting – web server was receiving loads and loads [at least a half a dozen per second] of page hits for a recently shared URL on a Facebook fanpage. “Oh, this traffic must be looking nice in Google Analytics real-time report” – I thought and logged into GA just to find out nothing but usual – regular traffic of a pageview each second or a few seconds…

After 24 hours of waiting on Google Analytics to consolidate data for previous day, few URLs shared previous on Facebook were examined and data in Apache website logs were compared to Google Analytics pageview reports. Discrepancy in pageviews for specific URLS between website Apache logs and GA was remarkable, to say the least:

For each shared URL data discrepancy was similar to this example:
~ 10K page GET requests for a page in Apache access logs
~ 3K pageviews in Google Analytics

At this point I thought that for some reason:

  • bots/crawlers have gone on a rampage
  • website might have been under some kind of [not so efficient] DDoS attack
  • Google Tag Manager (GTM) or GA codes were not fired properly

Testing hypotheses

Testing for “bots/crawlers” was fairly easy – a chunk of access log file [with request to a specific page] was submitted to an online log analyzer – LogCruncher. Crunched numbers disapproved hypothesis about bots/crawlers as 95% of all requests had different IP, and all those IPs were from ISP pools for standard users. So, the sources of traffic were regular users and their devices.

A deeper inspection of traffic was required. No patterns in missing traffic in Google Analytics could be found – all device categories, all manufacturers, all OSs, all browsers – they all had their share in Google Analytics, but still, a huge portion of traffic was missing. There must be a pattern, but it’s impossible to find it in Google Analytics.

 

Google Analytics Browser pivot by versions

 

For further Apache logs inspection and I used GoAccess – nice little Linux utility that does just what I needed – groups requests by various dimensions – this might help me in finding the pattern.

GoAccess OS overview

GoAccess: OS version distribution

 

And it did help… most of the requests were from Android, and since there were more requests from Android than there were missing pageviews – it was a strong indicator in which direction further analysis should go. From that point the rest was relatively easy – observed traffic was reduced to Android traffic only, and the next two hints were spotted right on.

These two hints also eliminated DDoS, since surge in page loads corresponded with link being shared on Facebook.

Facebook m.facebook.com as referal


One would say – that’s it
Facebook in-app browser blocks GTM or GA from firing.  A bold statement that needs strong arguments, which translates in more hypotheses to test. 🙂

A few quick tests were done – and  it proved that FB in-app requests were tracked as usual – GTM and GA fired as usual, _ga cookie was set and used. Could it be that GTM/GA in FB in-app are blocked just on some devices, while firing regularly on others?!

So I did two tests for this thesis using measurement protocol [did it by the book, using _ga cookie if available, etc.]:

  1. created measurement protocol [MP] hit [using image] and put it in NOSCRIPT on the page
    • this one would catch those that have JS disabled
    • MP was fired using specific data source parameter
  2. created measurement protocol [MP] hit [using image] and fired using JS if there were no GTM and GA objects on the page
    • this one would catch those that have JS enabled, but have GTM/GA blocked using adblocker/router etc.
    • MP was fired using specific data source parameter
  3. benchmark image: created measurement protocol [MP] hit on a different GA property, and fire it using plain image placed somewhere in BODY
    • this hit would be used as benchmark – since it would fire as image tag on every page view in client’s browser, it should collect regular + NOSCRIPT + GTM/GA blocking requests [except for those who block image loads]
  4. benchmark server-side call: created measurement protocol [MP] hit on a different GA property, and fire it during page generation on the server [complete server-side hit, does not depend on web client]
    • this hit would be used as absolute benchmark – if there’s a GET request – it would be executed (in case there are no issues with network traffic ;-))


Theoretically, this would would cover all possible cases, and after the test I would create custom reports based on
data source parameter and would look for patterns. The cases are:

  • GTM/GA are executed as usual
  • JS is blocked [GTM/GA are not executed], but MP is fired instead (via image)
  • GTM/GA are blocked, but since JS is enabled – it will create MP image if GTM/GA are not initialized
  • benchmark MP image is fired and should have ALL page views and match Apache logs 100% [since MP call is server side and does not depend on
  • server-side benchmark is fired on every GET request for a page

The Results

I thought that I will finally get ~100% match between Apache logs and GA. The result?!

Almost no increase! A slight, ~5% increase in number of pageviews in GA, so instead 10K vs. 3K, it was 10K vs. 3.5K – probably due to collecting those who block GTM/GA (well, I finally got GTM/GA blocking rate for the site ;-)).

Benchmark image MP hit should be 10K, right?! Well – benchmark image  GA property was showing 3.5K pageviews. That meant that FB in-app did not send request for a regular image on a page!

Benchmark server-side MP:  the only benchmark that was ~100% match with Apache logs was server-side MP call that was put in the main app code. No surprises here.

So, at this point I had these pieces of a puzzle::

  • HTTP referer: “http://m.facebook.com
  • User-Agent has: FB_IAB/FB4A;FBAV (Facebook in-app browser for Android)
  • GTM/GA are not loaded
  • regular images are not loaded – FB in-app requests only for HTML page

More questions, until…

So, a large portion of traffic was still missing, and I was stuck – there were no more information from the Apache log files that could be squeezed out, and I could not recreate situation where my traffic in Facebook in-app would be logged in Apache, but not in GA.

I did not know whether this traffic is valid or not, should I block it or not, and blocking by User-Agent  signature would have a large number of false-positives (this would block regular traffic as well).  

Test traffic on devices I made had the same HTTP referer and User-Agents had the same pattern [FB_IAB/FB4A;FBAV], but that traffic was tracked and visible in Google Analytics. I was not able to recreate something that was happening to ⅔ of visitors, no matter on how many devices I tested Facebook app.

There was just one more thing to inspect – HTTP request headers. Made a little HTTP header request logging routine, and started collecting HTTP headers. And … bingo!

[stripped out not so important parts of the HTTP request]
X-Purpose: preview
x-fb-sim-hni: xxyyy
x-fb-net-hni: xxyyy
Cache-Control: no-cache
referer: http://m.facebook.com/
cookie: _ga=GA1.2.xxxxxxxxx.yyyyyyyyy;
accept-encoding: gzip, deflate
x-fb-http-engine: Liger
Connection: keep-alive
  • x-fb-net-hni & x-fb-sim-hni – used for MCC+MNC (Mobile country code + Mobile network number – identifies country and provider)
  • x-fb-http-engine: Liger – this is the identifier of the engine, BUT, FB in-app does not send it every time! It is being sent only when X-Purpose is sent:
  • X-Purpose: preview – page prerender/prefetcher HTTP header

These are the pieces that were missing to properly identify missing requests in Google Analytics. Puzzle-pattern is now complete!

Conclusion

Facebook Android in-app sends SOME page requests with X-Purpose: preview header (and identifies as x-fb-http-engine: Liger), probably for prefetch/preview purposes [faster load for users]. Presumeably FB in-app has some internal algorithm (similar to Chrome’s prerender) that decides what will be preloaded and when. More on harnessing FB in-ap pressure and how browser’s prefetch/prerender can be used to speed up (Magento) sites will be covered in future articles – stay tuned! 

We all know that Magento CPU cycles come at a price, a hefty one. You should test on your own whether this FB in-app feature has implications on your web server performance and expenditure.

Are you affected?

How to test it on your site?! Here’s help for configuring Apache web server, something similar could be done with nginx as well.

  • edit main Apache config [search for LogFormat, and add new LogFormat]
    LogFormat "%h %l %u %t \"[%{x-fb-http-engine}i | %{X-Purpose}i]\" \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" fbligerprev
  • edit virtual host config, search for CustomLog and just above that line add:
    SetEnvIf x-fb-http-engine "Liger" fbliger
    CustomLog /var/log/path-to-your-logs/access_log_fbliger fbligerprev env=fbliger

This will log all “Liger/preview” requests in a separate file which you can examine and make appropriate decisions on how you’ll be handling this type of traffic on your site.

Please, share your “liger rate” in the comments below. Thanks!

The log file /path/access_log_fbliger would look like:

image03

Interesting facts:

Fact #1: there was only one Google search result [googlewhack] for “x-fb-http-engine: Liger” at the time of writing this article + one AdWords ad 😉

Googlewhack

Fact #2: The liger is a hybrid cross between a male lion (Panthera leo) and a female tiger (Panthera tigris). The liger has parents in the same genus but of different species. [Wikipedia]

Is your site affected by this? Please, share your experience…

Update 04.10.2016.

Article on mitigating this FB in-app hammering can be found here [for both Apache and nginx]: Mitigating Facebook’s “x-fb-http-engine: Liger” site hammering using Apache or nginx

The post Is your website being hammered by Facebook and what liger has to do with it? appeared first on Inchoo.

]]>
http://inchoo.net/dev-talk/magento-website-hammering-facebook-liger/feed/ 19
Development and design under the same roof http://inchoo.net/magento/developers-designers-working-inhouse/ http://inchoo.net/magento/developers-designers-working-inhouse/#comments Thu, 11 Aug 2016 11:16:36 +0000 http://inchoo.net/?p=27448 Building a web shop on Magento is challenging. When you see a feature that looks “so simple that you could do it”, it was usually not as simple to build it as you think. To create that “simple” thing designer had to spend a great amount of time finding the right solution. After all, creating...

The post Development and design under the same roof appeared first on Inchoo.

]]>
Building a web shop on Magento is challenging. When you see a feature that looks “so simple that you could do it”, it was usually not as simple to build it as you think. To create that “simple” thing designer had to spend a great amount of time finding the right solution. After all, creating a feature which draws on that kind of comment means translating something very unorganized and complex to something simple and understandable. On the other hand, developer had to spend a lot of hours translating that solution into code. Designer and developer can find that beautiful solution by communicating, which is crucial in this case. And that kind of communication, the one which yields incredible and neat results, would be much harder if a designer and developer are not placed in the same location.

It is all about communication..

When designing a web shop, designers always want to create the best user experience that will be followed by a beautiful user interface. The idea sounds so simple but the whole beauty of it is that it is not. It takes a lot of research, planning, sketching, trying multiple solutions and in the end – checking if that desired layout and functionality is possible within Magento.

In most cases, it is possible. But, think about the following things as well: is it worth the time, the money, possible performance issues, messy code…? If we give up on that functionality, what is the adequate replacement that will leave both sides satisfied?

That is the moment when “face to face” working meetings make the process easier for both designers and developers and where magic of direct communication comes in handy.

By brainstorming we can find the best solution which will give great user experience without leaving out the great performance.

The importance of that regular communication is the most visible in the wireframe phase when most of the features, website’s structure and informational architecture are being arranged. Since I already wrote about process of designing a Magento web store, I will now explain how important it is to work together, both designer and developer, and why one of the key factors is working on the same location.

Wireframe and design phase

It is crucial for a designer to translate to a developer what is the main idea and a desired outcome. Designer will brief him on the conclusion of the conducted research, importance of the planned features and explain, “face to face“, with help of wireframes and animation, what should the final version look like.

In the first phase of the project, when wireframes are given to developers, dialog about features and transitions (and many other tiny details) happens. On the other hand, if they are designed in an independent design company and simply given to frontend developers, they can have trouble understanding the designer’s idea and why is something designed as it is.

Designer and developer go through every feature on every page for desktop, tablet and mobile resolutions and search for any possible issues that could happen while translating design into code. It is not enough just to email the prototype approved by a client and say “Here you go!” because it is never possible to translate everything in the prototypes. Few emails with notes can’t replace real conversation. In some cases, explanations and conversation in person are necessary to give developers the right course and keep them on track. Walking them through the design in person and explaining how it should work makes the process easier, neater and faster.

That is the moment when developer would ask questions that designer maybe oversaw and write down notes that could be useful while working. Because, in the end, we all have to be sure that something that is suggested to the client is doable.

Because of all that, first people that see wireframes are always front end and back end developers – even before the clients. Wireframes must be first approved by a front end developer who checks the “We can translate that into code!” box, then by the client, and after that it is time for the design phase.

While slicing the design some problems can occur. Something after all creates a problem and we have to create different approach and redesign it. No problem – designer immediately jumps on that and together with the front end developer finds the solution.

Final phase

Importance of being on the same location doesn’t reduce even after handing the final designs to developers. After developers are done with their work it is time for designer to take a look to make sure that everything is in place.

That is the part where all smallest details are arranged, the smallest issues were found and working side by side is crucial. Conversations about tiniest details and sometimes showing physically what should be moved where is something that couldn’t be arranged if the designer and developer are not on the same location.

That collaboration makes building a web shop as efficient as possible and being able to consult with other team members in person can save thousands of dollars and hours of redundant back and forth messaging.

 

The post Development and design under the same roof appeared first on Inchoo.

]]>
http://inchoo.net/magento/developers-designers-working-inhouse/feed/ 4
Free your cart, and the speed will follow http://inchoo.net/magento/ttfb/ http://inchoo.net/magento/ttfb/#respond Tue, 09 Aug 2016 11:23:36 +0000 http://inchoo.net/?p=27401 What if I told you that you can significantly improve the most important performance metric on your Magento site with a few clicks in the admin interface? “This sounds like a bad commercial…” you would surely mumble under your breath (but you would keep reading, because, that’s why bad commercials still exist). And, you would...

The post Free your cart, and the speed will follow appeared first on Inchoo.

]]>
What if I told you that you can significantly improve the most important performance metric on your Magento site with a few clicks in the admin interface?

This sounds like a bad commercial…” you would surely mumble under your breath (but you would keep reading, because, that’s why bad commercials still exist). And, you would be (partly) correct. Just like in a bad commercial, the initial claim suffers from some ifs and buts. However, there actually is such low hanging fruit in the gardens of Magento performance. Let me take you there.

The ifs and the buts

What is “the most important performance metric on a website”? TTFB (Time To First Byte).
Why? In short, because before the first byte of the requested page arrives, nothing at all happens in the browser. It’s just idle wait time, and users hate that. For the site to (at least) feel fast, it is very important to get that first byte from server to browser as quickly as possible.
What exactly is so slow and can be easily turned off? MAP (Minimum Advertised Price) [http://docs.magento.com/m1/ce/user_guide/catalog/product-price-minimum-advertised.html].
But? It shouldn’t be turned off if you are actually using it.
And, it only matters if the user has something in cart.

Still…

Most sites aren’t using MAP, so chances are you can turn it off. Users often do have something in cart, and you don’t want to irritate them with slowness at that moment. Minicart is usually shown on every page of a Magento site. So, if you can use this optimization, it will really give you the promised significant performance improvements on every page load.

So, what happens that slows the website down?

This little bugger is difficult to cache:

minicart

Magento needs to render it for every user, and when rendering, it needs to call a method named canApplyMsrp for every product in it. And that method is quite slow. However, if you turn off MAP, it is short-circuited, and has no effect on performance, as is shown in the following table:

MAP (0 items in cart) MAP (2 items in cart) MAP (5 items in cart) MAP turned off
Real Project ~350 ~550 ~850 ~350
Vanilla RWD ~150 ~400 ~700 ~150

Numbers are milliseconds to first byte on homepage. As can be seen, MAP has no effect when nothing is in cart, but, progressively slows down page rendering when items are added to cart. And, if you turn off MAP, the rendering time, no matter how many items in cart, is the same as if there is nothing in it.

How to turn it off?

Make sure you don’t need MAP feature first!

 System / Config / Sales / Minimum Advertised Price / Enable MAP -> No.
 Save Config.

There. Enjoy!

The post Free your cart, and the speed will follow appeared first on Inchoo.

]]>
http://inchoo.net/magento/ttfb/feed/ 0
Getting started with CSS Flexbox http://inchoo.net/magento/magento-frontend/getting-started-with-css-flexbox/ http://inchoo.net/magento/magento-frontend/getting-started-with-css-flexbox/#respond Wed, 03 Aug 2016 11:09:10 +0000 http://inchoo.net/?p=27357 The CSS3 Flexible Box Layout, or as a shorter and more widely recognised term – Flexbox, is a CSS box model optimised for designing user interface layout. It is the addition to the four basic layout modes, previously defined in CSS: block layout, inline layout, table layout, and positioned layout. But, in contrast to these...

The post Getting started with CSS Flexbox appeared first on Inchoo.

]]>
The CSS3 Flexible Box Layout, or as a shorter and more widely recognised term – Flexbox, is a CSS box model optimised for designing user interface layout.

It is the addition to the four basic layout modes, previously defined in CSS: block layout, inline layout, table layout, and positioned layout. But, in contrast to these earlier ones, this is the first CSS module designed for laying out complex pages.

Flexbox brings a lot of new stuff to the table and solves some of the existing problems in rather simple manner, especially in regards to:

  • Building a responsive/fluid layouts (while containing elements, such as images, maintain their position and size relative to each other)
  • Vertical and horizontal centering/aligning of elements
  • Changing direction and reordering elements on the screen in a different order from how they are specified by the DOM (i.e., without altering the HTML)

In the rest of this article, I’m going to cover some of the flexbox’s defining aspects and also go through a couple of quick examples and practical ideas.

Flexbox basics

Two fundamental concepts essential for understanding flexbox are flex container & flex items.

flex-container1

To enable flexbox, first we need to specify the value on a display property for the container element.

.container {
    display: flex;
}

By doing so, we are changing container element to use flexbox layout, rather then default block layout. This also means that all container’s immediate child elements are automatically laid out by using flex layout model – they are now set to flex items.

Flexbox has a direction-agnostic layout algorithm and that is in contrast to the block layout – which has vertical direction, or inline layout – which lays out it’s elements horizontally.

Considering this, we can use flex-direction property to easily modify direction of elements:

.container {
    display: flex;
    flex-direction: row;
}

flex-row-1

As we can see on the previous image, it’s value is set to row  (which is a default value), but we can also change it to column:

.container {
    display: flex;
    flex-direction: column;
}

flex-column-1

By looking at most useful flexbox properties, we can also point out to justify-content; a property, which aligns flex items inside of the container on the main axis (horizontally):

.container {
    display: flex;
    justify-content: flex-start;
}

flex-start-1

On the contrast, for aligning flex items on the cross axis (vertically), we can use align-items property:

.container {
    display: flex;
    align-items: flex-end;
}

flex-end-1

As you may have noticed, we are modifying the layout by changing only one property, particularly the one which belongs to flex container.

Further modifications with flexbox can also be achieved by using properties for flex items.

For example, one of them is with flex-grow property, which defines what amount of the available space (inside the flex container) should the flex item occupy. All flex items have a default value of 0 and when we put value of 1 to each one of them, they will occupy the same amount of space in the container. If we put value of 2 to our 2nd item (leaving the other items with value of 1), it would take twice as much space as the others (if there is available space)

.container {
    display: flex;
}
 
.item1,
.item3 {
    flex-grow: 1;
}
 
.item2 {
    flex-grow: 2;
}

flex-grow-1

To put all together, based on the previous examples, we can conclude that flexbox properties are divided in the two main groups:

  1. Properties for the parent (flex container):
    • display
    • flex-direction
    • flex-wrap
    • flex-flow
    • justify-content
    • align-items
    • align-content
  2. Properties for the children (flex items):
    • order
    • flex-grow
    • flex-shrink
    • flex-basis
    • flex
    • align-self

It’s also useful to add that, since we are switching to using new flexbox layout, some properties that were created with the regular block layout in mind, won’t be applying here anymore (in this new context of flex layout). This means that:

  • float and clear do not create floating or clearance of flex item, and do not take it out-of-flow
  • vertical-align has no effect on a flex item
  • the ::first-line and ::first-letter pseudo-elements do not apply to flex containers, and flex containers do not contribute a first formatted line or first letter to their ancestors

Browser support

When getting started with new features in CSS, one of the first questions which comes to our mind is the one that concerns browser support. How do we stand here?

At the moment of writing this article, we can say that flexbox has reached a level in which all major browsers, in their latest versions, are supporting this functionality. Of course, as always, here we have a league of it’s own – the legacy browsers – or particularly, Internet Explorer. Although there are some known issues in the latest versions (which can be passed with certain workarounds), it’s pretty safe to say that if you’re lucky with having to maintain IE 10+, you’re good to go.

For supporting as much browsers as possible, like with most new CSS additions, flexbox requires us to add certain vendor prefixes. Fortunately, with modern frontend tools such as Autoprefixer – which automatically adds vendor prefixes to CSS rules that requires them (by using up-to-date values from caniuse.com), this is not a problem at all and you can easily forget about any cross-browser syntax differences.

So, how can we apply this set of tools on our websites? Let’s take a quick look at couple of short examples.

Reordering elements

The first is one of the most straight-forward examples of flexbox in action. Here we’re using the order property, which gives us the possibility to efficiently rearrange the whole layout, without the need for changing the HTML markup.

On the following image (screenshots are taken from Magento’s demo site), we are looking at small ‘promos’ list consisting of 3 items. On the smaller screens the list items are stacked in column, on top of each other, and on the larger screens, they are layed out in a row.

flex-order-marked-1

In some situations (e.g., when developing responsive websites) we might want to change the order of their appearance and we can easily do this just by setting the order property on the container’s child elements (flex-items).

Let’s say that we want to keep the default order of items on desktop, but on mobile screens we want to put the 1st item on bottom of the container. A default value for order property of all flex items is 0, so we’ll just need to give a value of 1 to the 1st item and, since it will now have a highest value, it will go down to the bottom.

ul.promos {
    display: flex;
}
 
ul.promos > li:first-child {
    order: 1;
}
 
@media (min-width: 768px) {
    ul.promos > li:first-child {
        order: 0;
    }
}

flex-order-marked-2

Sticky footer

The common problems when building a layout is a situation in which the page’s content is shorter than the height of the browser so we end up with the footer that sits up in the middle of the page (instead of at the bottom, where we want it to be).

Since this is a very old issue in front end development, there are lot of different existing solutions for this problem. Flexbox solves it in a simplest possible way.

We have made a search on Magento demo site with intention to get a ‘no-result’ search page in return. In the screenshot below, you can see that the main content area (main-container) has very small amount of content, and therefore the footer is pulled up.

sticky-footer-marked-1

To set things up, we need to take two steps.

First, as always when initializing flexbox, we’ll set up flex container (in this case on our page container) with flex-direction set to column and also declare min-height of 100vh, which will set our page container’s height to be the same as the height of the window.

For the second, and last step, we’ll put to our main-container value of flex property to 1. This will tell browser to expand our content area as needed (not depending on our lack of content).

.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
 
.main-container {
    flex: 1;
}

So, when we apply our code, we’ll see that footer gets sticked to the bottom of the page regardless of the amount of content on the page.

sticky-footer-marked-2

Equal height columns

Third example gives us an insight in a way that flexbox handles the column/box height.

We’re going to take a look at Magento product listing page, where each listed product has different content, with different height.

flex-equal-height-marked-1

Here we want to make each product box in one row the same height (each box will have the height of the highest box) and also get price box stacked to the bottom of the box.

To achieve this with the help of flexbox, this is what we need to do:

ul.products-grid {
    display: flex;
    flex-wrap: wrap;
}
 
ul.products-grid > li.item {
    display: flex;
    flex-direction: column;
}
 
.price-box {
    margin-top: auto;
}

With flexbox, equal height columns come out-of-the-box. To make it work, all it takes is to put display: flex on the container and all of its children (flex-items) will automatically stretch to the height of the parent element. We’ve also added flex-wrap: wrap onto the container to enable the list items to wrap into the multiple rows.

To fully stretch the content inside of the box (flex-item), we’ll also have to turn them into a flex container (with display: flex) and lay its content into the column (flex-direction: column).

For the last part, since we wanted the price box to be at the bottom of the box, we’ve added margin-top: auto to this element;

In the end, it gave a more organized look to our product list.

flex-equal-height-marked-2

Conclusion

This was just a basic overview and there is a lot more to cover. But, I hope it will be a good jump-start into the more advanced use of flexbox’s set of tools. Like with learning each new skill, it takes some time and practice to become a master, but eventually, you’ll get comfortable with it. There is a broad range of possibilities and I’m sure that you’ll also find some other flexbox use cases applicable for your website.

 

The post Getting started with CSS Flexbox appeared first on Inchoo.

]]>
http://inchoo.net/magento/magento-frontend/getting-started-with-css-flexbox/feed/ 0
Extending javascript methods Magento way http://inchoo.net/magento/extending-javascript-methods-magento-way/ http://inchoo.net/magento/extending-javascript-methods-magento-way/#comments Tue, 26 Jul 2016 11:45:18 +0000 http://inchoo.net/?p=27205 Hello, in this article I am going to show you how to properly extend javascript files in Magento 1. We’ve already covered the topic of extending Magento 2 default JS components in this article, but it’s time to revise how it’s done in Magento 1. So, let’s get to it!  For examples we are going...

The post Extending javascript methods Magento way appeared first on Inchoo.

]]>
Hello, in this article I am going to show you how to properly extend javascript files in Magento 1. We’ve already covered the topic of extending Magento 2 default JS components in this article, but it’s time to revise how it’s done in Magento 1. So, let’s get to it! 

For examples we are going to use prototype’s validation script located under js/prototype/validation.js and prototype javascript file located under js/prototype/prototype.js.

Extending javascript in Magento is resolved by loading the javascript files with rewritten classes and methods after the original ones. Common improper way of extending javascript is copying whole files that contain methods or classes we want to extend and making changes where we need them. This causes code pool to increase and it is completely unnecessary.

There are two ways we can extend Magento build in javascript, depending on whether the class we are extending is already extended in original files, and on what way.

Lets get it started!

If the class has not been extended using prototype’s Object.extend() method, we can use Class.create() method which allows us to call parent method when extending by using $super variable as an first and extra argument. For example we can use Ajax.Request class located in js/prototype/prototype.js.

First create a new file under js/inchoo/prototype.js in project root:

/*
 * INCHOO extend of prototype.js
 */
 
var InchooAjaxRequest = Class.create(Ajax.Request, {
    request: function($super, url){
        // your changes before
        $super(url); // call method from parent class
        // your changes after
    }
});
 
// replace parent class with extended class where needed and call the extended method
var ajaxBase = new InchooAjaxRequest();
ajaxBase.request('http://yourtesturl.com');

Next include it in your layout where it is needed, for example in local.xml. Also make sure it is included after the original (js/prototype/prototype.js in our case):

<default>
	<!-- rest of the layout -->
        <reference name="head">
            <action method="addJs"><script>inchoo/prototype.js</script></action>
	</reference>
        <!-- rest of the layout -->
</default>

 Second way is used if the class we are extending has already been extended using the prototype’s Object.extend() method, then we can extend it again using the same method.

First create a new file under js/inchoo/validation.js in project root:

/*
 * INCHOO extend of validation.js
 */
 
// check if Validation class exists and is property of current window object
// (we use this to double check we're not extending object that doesn't exist)
if('Validation' in window) {
    Object.extend(Validation, {
        test : function(name, elm, useTitle) {
            // your changes START
            Validation.whatDoesThisButtonDo();
            // your changes END
        },
        isVisible : function() {
            // you can extend multiple methods!
        },
        whatDoesThisButtonDo : function() {
            // or add new methods!
 
            // what does this button do?
            console.log('bam!');
        }
    });
}

Next, include it in your layout where it is needed, for example in local.xml. Also make sure it is included after the original (js/prototype/validation.js in our case):

<default>
	<!-- rest of the layout -->
        <reference name="head">
            <action method="addJs"><script>inchoo/validation.js</script></action>
	</reference>
        <!-- rest of the layout -->
</default>

Important part being the:

<action method="addJs"><script>inchoo/validation.js</script></action>

This way you only extend methods you need, and not the whole class as you would by just copying original file and then editing it. And what is great, you can extend multiple methods + add new ones you need. And that’s it!

First way is better because you can use $super variable to use parent class methods, but you can’t use it always as the second way.

If you have issues with it, first make alert or console.log on top of extended javascript file just to be sure you’re including the file correctly.

Good luck!

The post Extending javascript methods Magento way appeared first on Inchoo.

]]>
http://inchoo.net/magento/extending-javascript-methods-magento-way/feed/ 1
Product links on Magento CSV import http://inchoo.net/magento/product-links-magento-csv-import/ http://inchoo.net/magento/product-links-magento-csv-import/#respond Tue, 19 Jul 2016 11:29:28 +0000 http://inchoo.net/?p=27290 When I started working at Inchoo, one of our clients requested a functionality to be added to Magento CSV product import. Client wanted to know if an attribute value of one of the products that they were importing already had a matching value in the database and they also wanted to be able to jump to...

The post Product links on Magento CSV import appeared first on Inchoo.

]]>
When I started working at Inchoo, one of our clients requested a functionality to be added to Magento CSV product import. Client wanted to know if an attribute value of one of the products that they were importing already had a matching value in the database and they also wanted to be able to jump to that product via link. Find out more about handling this specific situation.

The attribute in question was a custom SKU that they used internally within the company, and as they were importing large number of products they didn’t want to have two products with the same custom SKU.

The biggest problem about this task was the issue of available memory as the number of the products in the database and the size of CSV files were large. In the code below I’ll focus only on the logic that was needed to make this work. For purpose of demonstrating this I’ll be using try() section of the validateAction() in Mage_ImportExport_Adminhtml_ImportController. 

First, we have to make the source file (CSV) available to us by adding another step to the source validation process:

$source = $import->setData($data)->uploadSource();
$validationResult = $import->validateSource($source);

Now there is a check whether the file is valid or not, but in our case the information about the duplicate attribute values should be displayed regardless of the validation result, so we will have to have the same logic in two places.

Second, we have to get the product data we need from Magento database. For this example we will be using ‘name’ attribute as the search value.

//-- set search value flag
$search_value_exists = 0;
 
//-- search_values array for storing search values from all products
$search_values = array();
 
$products_data = Mage::getResourceModel('catalog/product_collection')
		 ->addAttributeToSelect('name')
		 ->addAttributeToFilter('visibility', array('neq' => 1));

We will have to limit number of products that we will be loading while collecting the data from the database to avoid the memory issues. For this example a limit of 100 products per page is used.

//-- set page limit to 100
$products_data->setPageSize(100);
 
//-- get the number of pages
$pages = $products_data->getLastPageNumber();
$currentPage = 1;

And now we will get all the necessary data. The reason for taking SKU values is for uniqueness (other unique values can also be used), as the search values themselves may not be unique (e.g. two products may have the same name). After each page of 100 products is done, we clear the collection and load the next one.

do{
	$products_data->setCurPage($currentPage);
	$products_data->load();
 
	foreach($products_data as $product_data){
		$search_values[$product_data->getId()] = array('sku' => $product_data->getSku(),
							       'name' => $product_data->getName());
	}
 
	$currentPage++;
	$products_data->clear();
} while ($currentPage <= $pages);

For our example we will be displaying the product links with SKUs, so a link between search value and SKU will have to be stored in an array.

//-- array for storing skus belonging to duplicate search values
$sku_search_value = array();

Next, we open the CSV file and get column keys for SKU and search value.

//-- open csv file
$fh = fopen($source, 'r');
 
//-- get key for the search value and sku from the first row of csv
$rowData = fgetcsv($fh, 0, ',', '"');
$search_value_key = array_search('name', $rowData);
$sku_key = array_search('sku', $rowData);

Now comes the part where we will read CSV file row by row and compare the search value with the data that we got from the database (row by row reading of CSV file is used to avoid memory issues). Again, SKU here is used as a unique field to differentiate between those products that have the same search value (e.g. two different products have the same name). If the duplicate is found, then the link is created and stored within an array that will be used later to generate links to products.

//-- for each csv search value (excluding the first row - the column names)
//-- check if that search value exists within the search_values array
while ($rowData = fgetcsv($fh, 0, ',', '"')){
	$rowArray = array('sku' => $rowData[$sku_key],
		          'name' => $rowData[$search_value_key]);
 
	if(in_array($rowArray, $search_values)){
		$link = Mage::helper('adminhtml')
			->getUrl('adminhtml/catalog_product/edit',array('id' => array_search($rowArray, $search_values)));
 
		//-- if there is need for link to open a specific tab then the line below can be uncommented and modified
		//-- the group number can be found in eav_attribute_group table and other tabs will have their specific names
		//-- (like inventory) that can be found by looking in respective layout xml files and searching for <action method="addTab">
		//$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_group_8/'.substr($link, strpos($link, '/key/')+5, strlen($link));
 		//$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_inventory/'.substr($link, strpos($link, '/key/')+5, strlen($link));
 
		$sku_search_value[] = $rowData[$sku_key].' ('.'<a target="_blank" rel="external" href="'.$link.'">'.$rowData[$search_value_key].'</a>'.')';
		$search_value_exists = 1;
	}
}

After that, we close the CSV file and add the links to products with duplicate search values. The import button is disabled by setting the second parameter to false.

//-- close csv file
fclose($fh);  
 
//-- if the search value exists do not show the import button and notify the user
if($search_value_exists){
	$resultBlock->addSuccess(
		$this->__('File is valid, but there are names that already exist in magento products for following SKUs (in CSV): '.'<br>'.implode(', ', $sku_search_value).'<br>'),
		false
	);
} else {
	$resultBlock->addSuccess($this->__('File is valid! To start import process press "Import" button'), true);
}

In the end the whole try() section of the validateAction() should look like this (modified parts are marked by START and END comments):

try {
	/** @var $import Mage_ImportExport_Model_Import */
        $import = Mage::getModel('importexport/import');
	//-- 1st modification START
        //$validationResult = $import->validateSource($import->setData($data)->uploadSource());
	$source = $import->setData($data)->uploadSource();
	$validationResult = $import->validateSource($source);
	//-- 1st modification END
 
	if (!$import->getProcessedRowsCount()) {
		$resultBlock->addError($this->__('File does not contain data. Please upload another one'));
	} else {
        	if (!$validationResult) {
			if ($import->getProcessedRowsCount() == $import->getInvalidRowsCount()) {
				$resultBlock->addNotice(
                                	$this->__('File is totally invalid. Please fix errors and re-upload file')
                            	);
                        } elseif ($import->getErrorsCount() >= $import->getErrorsLimit()) {
                        	$resultBlock->addNotice(
                                	$this->__('Errors limit (%d) reached. Please fix errors and re-upload file', $import->getErrorsLimit())
                            	);
                        } else {
                        	if ($import->isImportAllowed()) {
 
                                //-- 2nd modification START
                                //-- set search value flag
                                $search_value_exists = 0;
 
                                //-- search_values array for storing search values from all products
                                $search_values = array();
 
                                $products_data = Mage::getResourceModel('catalog/product_collection')
                                                ->addAttributeToSelect('name')
                                                ->addAttributeToFilter('visibility', array('neq' => 1));
 
                                //-- set page limit to 100
                                $products_data->setPageSize(100);
 
                                //-- get the number of pages
                                $pages = $products_data->getLastPageNumber();
                                $currentPage = 1;
 
                                do{
                                    $products_data->setCurPage($currentPage);
                                    $products_data->load();
 
                                    foreach($products_data as $product_data){
                                        $search_values[$product_data->getId()] = array('sku' => $product_data->getSku(), 'name' => $product_data->getName());
                                    }
 
                                    $currentPage++;
                                    $products_data->clear();
                                } while ($currentPage <= $pages);
 
                                //-- array for storing skus belonging to duplicate search values
                                $sku_search_value = array();
 
                                //-- open csv file
                                $fh = fopen($source, 'r');
 
                                //-- get key for the barcode and sku from the first row of csv
                                $rowData = fgetcsv($fh, 0, ',', '"');
                                $search_value_key = array_search('name', $rowData);
                                $sku_key = array_search('sku', $rowData);
 
                                //-- for each csv search value (excluding the first row - the column names)
                                //-- check if that search value exists within the search_values array
                                while ($rowData = fgetcsv($fh, 0, ',', '"')){
                                    $rowArray = array('sku' => $rowData[$sku_key],
                                                      'name' => $rowData[$search_value_key]);
 
                                    if(in_array($rowArray, $search_values)){
                                        $link = Mage::helper('adminhtml')
                                                ->getUrl('adminhtml/catalog_product/edit',array('id' => array_search($rowArray, $search_values)));
 
                                        //-- if there is need for link to open a specific tab then the line below can be uncommented and modified
                                        //-- the group number can be found in eav_attribute_group table and other tabs will have their specific names
                                        //-- (like inventory) that can be found by looking in respective layout xml files and searching for <action method="addTab">
                                        //$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_group_8/'.substr($link, strpos($link, '/key/')+5, strlen($link));
                                        //$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_inventory/'.substr($link, strpos($link, '/key/')+5, strlen($link));
 
                                        $sku_search_value[] = $rowData[$sku_key].' ('.'<a target="_blank" rel="external" href="'.$link.'">'.$rowData[$search_value_key].'</a>'.')';
                                        $search_value_exists = 1;
                                    }
                                }
 
                                //-- close csv file
                                fclose($fh);  
 
                                //-- if the search value exists do not show the import button and notify the user
                                if($search_value_exists){
                                    $resultBlock->addNotice(
                                        $this->__('Please fix errors and re-upload file. File also contains names that already exist in magento products for following SKUs (in CSV): '.'<br>'.implode(', ', $sku_search_value).'<br>'),
                                        false
                                    );
                                } else {
                                    $resultBlock->addNotice(
                                        $this->__('Please fix errors and re-upload file or simply press "Import" button to skip rows with errors'),
                                        true
                                    );
                                }
                                //-- 2nd modification END
                            } else {
                                $resultBlock->addNotice(
                                    $this->__('File is partially valid, but import is not possible'), false
                                );
                            }
                        }
                        // errors info
                        foreach ($import->getErrors() as $errorCode => $rows) {
                            $error = $errorCode . ' ' . $this->__('in rows:') . ' ' . implode(', ', $rows);
                            $resultBlock->addError($error);
                        }
                    } else {
                        if ($import->isImportAllowed()) {
                                //-- 3rd modification START
                                //-- set search value flag
                                $search_value_exists = 0;
 
                                //-- search_values array for storing search values from all products
                                $search_values = array();
 
                                $products_data = Mage::getResourceModel('catalog/product_collection')
                                                ->addAttributeToSelect('name')
                                                ->addAttributeToFilter('visibility', array('neq' => 1));
 
                                //-- set page limit to 100
                                $products_data->setPageSize(100);
 
                                //-- get the number of pages
                                $pages = $products_data->getLastPageNumber();
                                $currentPage = 1;
 
                                do{
                                    $products_data->setCurPage($currentPage);
                                    $products_data->load();
 
                                    foreach($products_data as $product_data){
                                        $search_values[$product_data->getId()] = array('sku' => $product_data->getSku(),
                                                                                       'name' => $product_data->getName());
                                    }
 
                                    $currentPage++;
                                    $products_data->clear();
                                } while ($currentPage <= $pages);
 
                                //-- array for storing skus belonging to duplicate search values
                                $sku_search_value = array();
 
                                //-- open csv file
                                $fh = fopen($source, 'r');
 
                                //-- get key for the barcode and sku from the first row of csv
                                $rowData = fgetcsv($fh, 0, ',', '"');
                                $search_value_key = array_search('name', $rowData);
                                $sku_key = array_search('sku', $rowData);
 
                                //-- for each csv barcode (excluding the first row - the column names)
                                //-- check if that barcode exists within the barcodes[] array
                                while ($rowData = fgetcsv($fh, 0, ',', '"')){
                                    $rowArray = array('sku' => $rowData[$sku_key],
                                                      'name' => $rowData[$search_value_key]);
 
                                    if(in_array($rowArray, $search_values)){
                                        $link = Mage::helper('adminhtml')
                                                ->getUrl('adminhtml/catalog_product/edit',array('id' => array_search($rowArray, $search_values)));
 
                                        //-- if there is need for link to open a specific tab then the line below can be uncommented and modified
                                        //-- the group number can be found in eav_attribute_group table and other tabs will have their specific names
                                        //-- (like inventory) that can be found by looking in respective layout xml files and searching for <action method="addTab">
                                        //$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_group_8/'.substr($link, strpos($link, '/key/')+5, strlen($link));
                                        //$link = substr($link, 0, strpos($link, '/key/')).'/tab/product_info_tabs_inventory/'.substr($link, strpos($link, '/key/')+5, strlen($link));
 
                                        $sku_search_value[] = $rowData[$sku_key].' ('.'<a target="_blank" rel="external" href="'.$link.'">'.$rowData[$search_value_key].'</a>'.')';
                                        $search_value_exists = 1;
                                    }
                                }
 
                                //-- close csv file
                                fclose($fh);  
 
                                //-- if the search value exists do not show the import button and notify the user
                                if($search_value_exists){
                                    $resultBlock->addSuccess(
                                        $this->__('File is valid, but there are names that already exist in magento products for following SKUs (in CSV): '.'<br>'.implode(', ', $sku_search_value).'<br>'),
                                        false
                                    );
                                } else {
                                    $resultBlock->addSuccess(
                                        $this->__('File is valid! To start import process press "Import" button'), true
                                    );
                                }
                                //-- 3rd modification END
                        } else {
                            $resultBlock->addError(
                                $this->__('File is valid, but import is not possible'), false
                            );
                        }
                    }
                    $resultBlock->addNotice($import->getNotices());
                    $resultBlock->addNotice($this->__('Checked rows: %d, checked entities: %d, invalid rows: %d, total errors: %d', $import->getProcessedRowsCount(), $import->getProcessedEntitiesCount(), $import->getInvalidRowsCount(), $import->getErrorsCount()));
                }

 The end result for a valid CSV file would look something like this:

import_product_links

Have you ever met with a situation like this? What’s your take on it?

The post Product links on Magento CSV import appeared first on Inchoo.

]]>
http://inchoo.net/magento/product-links-magento-csv-import/feed/ 0
Can Sketch help us improve our design workflow? http://inchoo.net/magento/design/can-sketch-help-us-improve-our-design-process/ http://inchoo.net/magento/design/can-sketch-help-us-improve-our-design-process/#comments Wed, 13 Jul 2016 11:50:12 +0000 http://inchoo.net/?p=27211 Designers are constantly trying to develop a healthy design process by experimenting with tools in an attempt to find the one that works best. I’ve been using Photoshop as my primary design tool for almost 10 years, and the fact that Photoshop wasn’t created for UI design was never a real problem. Why? Because Adobe...

The post Can Sketch help us improve our design workflow? appeared first on Inchoo.

]]>
Designers are constantly trying to develop a healthy design process by experimenting with tools in an attempt to find the one that works best.

I’ve been using Photoshop as my primary design tool for almost 10 years, and the fact that Photoshop wasn’t created for UI design was never a real problem. Why? Because Adobe was always throwing new features to support UI design. The main problem is that Photoshop’s engine was never optimized for UI design, it is huge, memory-consuming, and many designers weren’t pleased with it’s performance.

The more large files you work on, the more you can get stuck and that was the main reason to look out for new and better alternatives to an existing design workflow.

Arrival of Sketch

A few years ago Sketch has become one of the favorite topics for discussion in UI design community. Sketch is 100% vector, lightweight, fast, focused solely on working on UI design and built exclusively for Mac by Bohemian Coding.

Almost two years ago I decided to give Sketch a try, but after a few days, initial enthusiasm began to wear off. It was buggy, messy and very frustrating. I still remember very weird bug when text disappeared suddenly or become uneditable. The only way to fix that was to close and reopen the file.

I’ve lost too much time on a project with Sketch and decided to move back to Photoshop in the middle of design process.

It was obvious it has many potential, but it wasn’t 100% ready to use for production. I decided to wait some time because it was getting better and better with every update. Finally, this year, after Sketch 3.7 was released, I decided to give it another try.

How creating a website in Sketch improves the design process?

This is not going to be another “Sketch vs Photoshop” article with features cited against each other. In this post, I’ll briefly explain our thoughts and insights into Sketch, and we’ll take a dive into key features in future posts.

Interface and Artboards

Sketch’s interface is clean and simple, it offers only what can be recreated with HTML and CSS3 so everything is perfectly suited for interface design.
interface

First thing you’ll notice is Sketch’s infinite canvas. You can scroll as far as you want, so if you’re creating a responsive web design you may want to set up your working area by adding artboards first. Simply type the letter ‘A’ and the Inspector will show up with some common templates.

artboards

Creating separate working frames, with customisable built-in grids, in a single canvas makes Sketch perfect for designing responsive sites and apps.

Scalability and Pixel Precision

Sketch works in vectors so it is easy to rescale the objects on canvas without losing quality and you’ll also get precise pixel control making it perfect for design in a world of different screen sizes and pixel densities. If you want to see the non-vector degradation, just switch to View > Canvas > Show pixels and you’ll see equivalent to exporting the image to PNG and then zooming in.

Shared Styles and Symbols

Symbols allow you to save time and make designs way easier to maintain by reusing elements across Artboards and Pages. To create a symbol, simply select a group, artboard, or a selection of layers and click the Create Symbol icon in the toolbar.
Although they are still not powerful like Smart Objects in Photoshop (resizing is not available), they still allow you to improve your workflow. If you use Atomic Design approach by Brad Frost the symbols page will give you an overview of all building blocks.

symbols

Plugins

Let’s face it, Sketch still lacks many features we are used to, but there is amazing community of designers and developers who have created impressive range of Sketch plugins and I’ll go through some of the ones I find most useful.

The first plugin you need to download and install is Sketch Toolbox. It is a simple 3rd-party plugin manager which allows you to easily browse and install plugins without much hassle. With a single click it will download and install new plugins while also keeping up to date the previously downloaded ones.

toolbox

Dynamic Buttons
Let’s say we have a few buttons with the different labeling text and the same style. Dynamic button plug-in allows us to create buttons with fixed paddings so they automatically expand based on the text they contain.
Just create a text layer and use shortcut Cmd + J to create a dynamic button. After Dynamic Group is created, use CSS shorthands to define padding (e.g. 10:30:10:30) and use Cmd+J to update button. Now you can duplicate button and change text, to update just press Cmd + J and button will change automatically.

dynamic-button

Craft
Craft is a plugin suite created by InVision Labs which comes with three plugins: Data, Duplicate, and Library. It allows you to quickly pull in contextually relevant content in your designs, duplicate design elements quickly with pixel perfect accuracy, and keep your design library cloud-connected.

Data

craft-generating-content

Duplicate

craft-duplicate

States
If you’ll ask UI designers what feature they would like to see in the next upgrade I’m sure many would say they want an equivalent to Layer Comps. Unfortunately there isn’t that feature in Sketch at the moment, but Eden Vidal created great plugin called States and it works similar to Layer Comps (you can change positions and toggle visibility of layers).

states

Sketch Palettes
Color management system in Sketch is really simple, there’s just global colors and document colors. If you want to create a different palette for each project, you’ll have to use a plugin. One of the best is Sketch Palettes by Andrew Fiorillo.

You can save color palette from either the Global or Document Colors.

save-palette

Once you’ve saved some palettes, you can load them as needed by opening a .sketchpalette file containing the colors you want to load. This will replace current colors in the selected section. Saving palettes in the cloud is also a great way to share them between the team.

Exporting screens and integration with other tools

Many developers don’t have experience with Sketch yet, and since it is Mac only tool many of them won’t be able to open Sketch file if they are on other OS. For that reason, very often designers will have to slice up all of the assets for them. Fortunately, exporting the assets in Sketch is super-easy and it allows exporting multiple sizes and multiple formats.

export

You will also have to spend some time creating a style guide in order to prepare styles developers will need to get started. Again, this won’t take you more than one click in Sketch using Craft Library plugin. It’s going to look an entire document and create a style guide in the new page named Styles.

styles

Another option is to use collaboration tool like Zeplin. Sketch Zeplin plugin will send your artboards to project in Zeplin where you have up-to-date repository for collaboration with developers. Developers can click on a layer and see all the information.

Prototyping tools like InVision, Marvel, Framer and Principle have also their own Sketch integration, so you can turn your designs into interactive prototypes automatically without extracting your screens and assets.

Price

Comparing Sketch’s price to Adobe’s subscriptions revealed it’s cheaper in the long term. At this moment (v3.7.2) fixed price is $99 versus $19.99 a month for Photoshop CC. But it is important to mention that Sketch announced new Versioning and Licensing model. A license of Sketch will still cost $99 USD, but for one year of free updates.

The Future of Sketch

Sketch is not perfect yet, it does have a few downsides and lacks some useful features, but it is already a tool that makes design workflow much faster so we can focus on other aspects of design process. Also we shouldn’t forget it’s only a few years old and the team behind it releases new updates constantly. No one can reliably predict the future, but Sketch is definitely moving at a good pace. It will be interesting to see their reaction to Adobe’s XD, long-awaited design/prototyping tool which looks very promising too. One thing is for sure, the only one who will see a benefit from this war will be designers.

The post Can Sketch help us improve our design workflow? appeared first on Inchoo.

]]>
http://inchoo.net/magento/design/can-sketch-help-us-improve-our-design-process/feed/ 1
Meet Magento Association introduces exciting changes – Meet Magento 2.0 http://inchoo.net/ecommerce/exciting-changes-to-meet-magento-association-meet-magento-2-0/ http://inchoo.net/ecommerce/exciting-changes-to-meet-magento-association-meet-magento-2-0/#respond Wed, 06 Jul 2016 11:06:17 +0000 http://inchoo.net/?p=27182 Have you heard of Meet Magento Association – an amazing group of individuals who have done incredible things for the community around Magento, being an integral part of making it into what it’s become? They recently made some significant changes to the organization. Here’s the latest scoop on what’s new and how it affects the ecosystem!...

The post Meet Magento Association introduces exciting changes – Meet Magento 2.0 appeared first on Inchoo.

]]>
Have you heard of Meet Magento Association – an amazing group of individuals who have done incredible things for the community around Magento, being an integral part of making it into what it’s become?

They recently made some significant changes to the organization. Here’s the latest scoop on what’s new and how it affects the ecosystem!

Who/what is Meet Magento Association?

Now, you can’t tell me you haven’t visited any of the Meet Magento conferences that are happening around the world. If you have, like myself, had the privilege of attending, organizing or even speaking at some of those, you’re probably hooked and can’t wait to get the opportunity to visit new countries while meeting a lot of old friends.

I’ve been to two Imagine conferences in Vegas (and those were a blast) but Meet Magentos certainly have that special feeling – they’re usually smaller, cosier and you don’t have to spend days and weeks before the conference carefully planning your own schedule (and still, inevitably, missing on some good stuff).

Meet Magento events have been organized in more than 20 countries so far (there are quite a few coming up in the second half of 2016 so better start preparing). And while there are local event partners who actually put everything together, it all started with a small group of enthusiasts from Netresearch company based in Leipzig who organized the first Meet Magento Germany. Hats off to Thomas Fleck and Rico Neitzel for that, and I’m sure you’re also familiar with the tale of two Thomases that started once Mr. Fleck was joined by another Thomas – Goletz.

They were soon joined by Guido Jansen and Dutchento who pulled off Meet Magento Netherlands – and these two are now the longest-running events of the Magento ecosystem. So, they are the ones behind all of the Meet Magento events, and let’s not forget Developers Paradise, the event that has been talked about a lot this year 🙂

So, what’s new? Global organization

Officially announced at Meet Magento Germany in Leipzig this week – they’ve changed some of the roles and responsibilities, now have Magento as the official partner (details are yet to be revealed) and introduced a global organization with 3 regional VPs and an Innovation Officer.

Ok, why is that important?

This move should allow other members of the ecosystem to have more direct access to “their” VPs who can provide more immediate assistance, advice and guidance around the plans to organize new events or improve on the existing ones.

Here we have a lineup of Meet Magento veterans – Kuba and Guido have organized a lot of MM events in Poland and Netherlands and SmartOSC is a stronghold in South East Asia.

Historically, Meet Magento hasn’t done many events in USA (Interactiv4 organized several MMNY events there) so with this way of expanding we may soon see some new MM events popping up from the East coast to the West.

What’s that about Magento and official support again?

So far, Magento has kept somewhat of a distance from official organization of these types of events and everyone organizing needed to make a distinction that those were “community-driven” events.

However, at this year’s Magento Live UK something new was announced:

Apparently, there was more news about it at this year’s Meet Magento Germany and I’m looking forward to next week’s meeting with all Meet Magento event organizers when I’m guessing we’ll be officially introduced to a lot of these changes.

Meet Magento World – coming to your home!

There is one addition to the family of Meet Magento events – Meet Magento World – an online Magento conference scheduled for December 6-8 this year. This event plays perfectly into the new concept behind the association – it’s global, interconnected, and we’re all contributing to one common goal.

meet-magento-world-speakers

And the speaker avatars look great – not just because our Katarina prepared them :).

Of course, this event will be joined (my expectations) by mostly developers and other community members (I don’t see this as something that has the potential of drawing in many merchants, at least not in its inaugural year), but even so, it will serve its purpose. Have you signed up already?

What’s next?

Magento is proving to be much more community-orientated since the eBay breakup, and there is still some way to go with improving Magento 2 so it can truly be the safest bet for many merchants and agencies alike.

Having Meet Magento Association grow stronger and be recognized by Magento as a true helping hand should help us all write new, successful stories for developers, agencies and, most importantly, those who put bread on our table – the merchants.

So, where can we see you in person to share some stories? What’s the next Meet Magento event you are attending?

 

The post Meet Magento Association introduces exciting changes – Meet Magento 2.0 appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/exciting-changes-to-meet-magento-association-meet-magento-2-0/feed/ 0
The latest Magento and eCommerce news http://inchoo.net/ecommerce/news-magento-ecommerce/ http://inchoo.net/ecommerce/news-magento-ecommerce/#comments Thu, 30 Jun 2016 12:31:09 +0000 http://inchoo.net/?p=27133 What’s new in the Magento world and the world of eCommerce? Well we’re bringing you the latest scoop. Magento 2.1 This month, Magento came out with Enterprise Edition 2.1 which offers new features that simplify the use of eCommerce sites for both retailers and customers. The first feature is staging and preview which allows you to...

The post The latest Magento and eCommerce news appeared first on Inchoo.

]]>
What’s new in the Magento world and the world of eCommerce? Well we’re bringing you the latest scoop.

Magento 2.1

This month, Magento came out with Enterprise Edition 2.1 which offers new features that simplify the use of eCommerce sites for both retailers and customers. The first feature is staging and preview which allows you to schedule and preview updates to products, categories, descriptions and promotions. Another part of this feature is the new timeline dashboard that enables you to clearly see what is scheduled to happen on the site so you can track the sales process and later analyze the impact you had on it.

The second feature is Elasticsearch which ensures high quality search results and is easier to set up, scale and manage. This search engine supports 33 languages out-of-the-box, and allows you to set your own stop words, synonyms and attribute weighting to deliver better search results.

Another feature are PayPal enhancements for the streamlined checkout. That includes the implementation of PayPal In-context checkout, so cutomers can use PayPal without leaving your site. With this feature, the storing of credit card information can be done in a safe way.

The last key feature are the bussines management tools. They are made intuitive and easy to use in order to simplify the process for the retailers. With the drag-and-drop tools, retailers can customize and save admin views for a quick access to products, customers and other information.

eCommerce

Pinterest is launching a way to search for products with your smartphone camera. In the next few months, Pinterest is going to be rolling out an improved visual search button feature – that will allow you to automatically perform a visual search for all the items in the frame of your camera. This feature could be used to find the furniture you see on a TV show or in a restaurant, the clothes you see on a celebrity, or a person on the street (but in that case, don’t forget to turn the flash of) it can even work on car parts, the possibilities are endless.

When launched, this feature could drive Pinterest closer to its competitors. This visual tool eliminates a lot of steps a potential customer would usually go through. It coincides with the shift to micro moments instead of a long research process, and marketers turning to map making instead of storytelling. Pinterest is monetising on the fact that people live on-the-go and don’t necessarily research every item they buy. A new “shopping cart” feature, accessible across all platforms where the users have active accounts, will also play its part in enhancing the overall commerce experience.

In other news, Amazon just announced they are expanding the Dash button program. If you’re not familiar with them, Dash buttons are small devices that allow customers to place orders on specific products. Every button is specific to a different company. Now you’ll be able to get up to 150 different buttons from the likes of Lavazza, Play-Doh, Red Bull, Schwarzkopf and many more, meaning you’ll never have to leave your house to go shopping again.

Finally, at the end of this news cycle, we leave you with a question. Which of these features are you most excited for?

The post The latest Magento and eCommerce news appeared first on Inchoo.

]]>
http://inchoo.net/ecommerce/news-magento-ecommerce/feed/ 1