I recently reloaded my laptop and upgraded it to Sierra. As Apple continues to tighten down security measures, more and more programs are having trouble installing properly (or living through the upgrade process).

The problem I experienced was with installing VMWare Fusion: particularly with calling vagrant up. In doing so, I received the following cryptic error: “Error occurred: The file which defines networking interfaces for VMware Fusion could not be found. Please verify Fusion is installed properly and try again. If this problem persists, please contact support.” Additionally, when I tried to create a dummy VM in VMWare Fusion, I also received a cryptic “Internal error” message.

I found that the file /Library/Preferences/VMware Fusion/networking was non-existent. Since there was another machine that I use that works as expected, I copied the networking file over to my laptop. Vagrant booted the machine just like it was supposed to.

For your (and my) reference, here are the contents of the file:

VERSION=1,0
answer VNET_1_DHCP yes
answer VNET_1_DHCP_CFG_HASH D57F26DD20520022B91D6F44905AC85450450858
answer VNET_1_HOSTONLY_NETMASK 255.255.255.0
answer VNET_1_HOSTONLY_SUBNET 172.16.203.0
answer VNET_1_VIRTUAL_ADAPTER yes
answer VNET_8_DHCP yes
answer VNET_8_DHCP_CFG_HASH BA7AFE70F85B41C6F5C490924040A16C48AE08B9
answer VNET_8_HOSTONLY_NETMASK 255.255.255.0
answer VNET_8_HOSTONLY_SUBNET 172.16.32.0
answer VNET_8_NAT yes
answer VNET_8_VIRTUAL_ADAPTER yes
add_nat_portfwd 8 tcp 2200 172.16.32.159 22
add_nat_portfwd 8 tcp 2222 172.16.32.150 22

Magento 2 Template Paths

January 04, 2017

While working on a Magento 2 project yesterday, I encountered a scenario where I needed to change a block's template to be a custom template. In Magento 1, it's pretty easy to do this with the setTemplate() method:

<reference name="block_to_change">
    <action method="setTemplate">
        <template>path/to/your/template.phtml</template>
    </action>
</reference>

Magento 2 has similar functionality:

<referenceBlock name="order_shipping_view">
    <action method="setTemplate">
        <argument name="template" xsi:type="string"><![CDATA[The topic of the blog post.]]></argument>
    </action>
</referenceBlock>

You would then set the path to the template inside the <argument> node. This is a handy way to make UI-related customizations, without having to create a new block.

As any Magento developer could tell you, the path to your template in Magento 1 is pretty straightforward. Because templates are stored in the app/design/[area]/[package]/[theme]/[template] directory, you place the template anywhere inside the theme’s template directory. You then use its path, relative to the template directory, as the path in layout.

Since Magento 2 is more decoupled, it is approached a little differently. The Magento 2 module structure introduces the idea of templates inside a module’s directory (ModuleName/view/[area]/templates). Any templates your module depends on should be inside that directory. If a block’s template is in your module, the path will be relative to the ModuleName/view/[area]/templates directory.

However, if you are referencing a template for a block from another module, including core blocks (like Magento\Framework\View\Element\Template), the only path that works will be like this: ModuleName::path/to/your/template.phtml. In this example, the path/to/your/template.phtml part is relative to the view/[area]/templates directory in the module you are working with. The system won't find the template without the ModuleName:: part.

I was a bit puzzled when I found this. Why would the Magento 2 team choose an approach like this? And, what was going on in the Magento 2 core when it was attempting to locate a template?

Using PhpStorm and Xdebug to step through the process, I found the Magento\Framework\View\Filesystem::getTemplateFileName() method and quickly figured out the answer to my questions. Here's the code from that method:

     // Lines 108-126

     /**
     * Get a template file
     *
     * @param string $fileId
     * @param array $params
     * @return string|bool
     */
    public function getTemplateFileName($fileId, array $params = [])
    {
        list($module, $filePath) = \Magento\Framework\View\Asset\Repository::extractModule(
            $this->normalizePath($fileId)
        );
        if ($module) {
            $params['module'] = $module;
        }
        $this->_assetRepo->updateDesignParams($params);
        return $this->_templateFileResolution
            ->getFile($params['area'], $params['themeModel'], $filePath, $params['module']);
    }

Let's break that down. The method accepts a variable named $fileId, which I observed to be the template path from layout (the value set in the template argument above) and an array of parameters. The $fileId variable is passed to another method: Magento\Framework\View\Asset\Repository::extractModule(). Let's have a look at that method.

    //Lines 415-434

    /**
     * Extract module name from specified file ID
     *
     * @param string $fileId
     * @return array
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public static function extractModule($fileId)
    {
        if (strpos($fileId, self::FILE_ID_SEPARATOR) === false) {
            return ['', $fileId];
        }
        $result = explode(self::FILE_ID_SEPARATOR, $fileId, 2);
        if (empty($result[0])) {
            throw new \Magento\Framework\Exception\LocalizedException(
                new \Magento\Framework\Phrase('Scope separator "::" cannot be used without scope identifier.')
            );
        }
        return [$result[0], $result[1]];
    }

In the above code, the constant FILE_ID_SEPARATOR is defined as ::. At this point, the code becomes simple PHP: if the template path doesn't contain the string ::, an array with two elements is returned. The first element would be an empty string, and the second element would be the template path. If, however, the template file path has the string :: in it, it is split on that substring, and an array is returned with two elements. The first element is a string that contains the first part of the split template path. The second element contains the second part of the split template path.

At this point, you may be wondering what the returned value of that method has to do with anything. Let's look at the first method again:

    list($module, $filePath) = \Magento\Framework\View\Asset\Repository::extractModule(
        $this->normalizePath($fileId)
    );
    if ($module) {
        $params['module'] = $module;
    }

I've reposted the relevant lines here, but if you want to see the whole method, scroll up and review it.

Take a look at the first line there—the one with list(). PHP uses the list() construct to create and assign multiple variables from an array. The variable names that are passed into the parentheses will then be assigned to the elements of the array, in order. The Magento\Framework\View\Asset\Repository::extractModule() method returns an array with two elements. The first element will be either an empty string or a string containing the ModuleName part of a template file path. The second element will have the path/to/your/template.phtml part. The list() construct will assign the first element to the $module variable, and the second element to the $filePath module.

Next, the system checks if the $module variable is a string with contents, or empty. If it's not empty, the $params array will have an element, module, defined as the first part of the file path in the template.

Finally, the system passes some of the $params and the $filePath off to another class and method that actually takes that information and finds the template file.

Let's recap what we've seen up to this point: Magento 2 tries to extract a module name from the first part of the template path. If it finds one, it will pass that as the module that contains the template.

But, what happens if there's no first part in that template path or if the system never finds the :: substring? The answer is in the method that calls the first function we looked at. The first function was Magento\Framework\View\Filesystem::getTemplateFileName(), and the method that calls it is Magento\Framework\View\Element\Template::getTemplateFile(). Here it is:

    //Lines 199-207

    public function getTemplateFile($template = null)
    {
        $params = ['module' => $this->getModuleName()];
        $area = $this->getArea();
        if ($area) {
            $params['area'] = $area;
        }
        return $this->resolver->getTemplateFileName($template ?: $this->getTemplate(), $params);
    }

This is where the $params array is initialized. Do you see, in the first line, how the default module element is provided by another function (getModuleName)? In Magento 2, Magento\Framework\View\Element\Template is the generic template block, very similar to Mage_Core_Block_Template in Magento 1. All that function (getModuleName) does is return the name of the module that the current block is from.

If the template path doesn't provide directions on what module to find the template in, the core code assumes that the template is associated with the same module as the block. This is why it's so important to include ModuleIdentifier:: before the template path when changing a block's template. It is the same as prepending your module name to the template path.

We started this deep dive with two questions: Why would the team behind Magento 2 choose an approach like this? And, what was going on internally to the Magento 2 codebase when the system was trying to locate a template? While the core team is the only people who can definitively answer question number 1, it seems to allow for a great degree of modularity, especially in comparison to the old app/design template structure. And, I think we've seen what goes on internally in finding a template!

I'll close with a best practices tip: I think using the ModuleIdentifier:: syntax is a good thing to be in the habit of, even when it's not required. I can see it keeping things more clean and clear. Happy coding!

Attributes are a key part of the Magento ecosystem. These little bits and pieces of data help describe products. One of the easiest-to-understand attributes would be attribute “color”. Another common attribute is “size”. Not every product on your website will have a value for the attribute “color” or “size”, but many likely will.

For anyone who is managing a Magento storefront on a daily basis, you have worked with attributes. Not only adding new values (perhaps a new size is available for a popular sweater that you sell), but managing existing values (perhaps you are no longer going to carry the color “red”).

Let’s say you needed a list of all the products on your web site that have a value for “color” (regardless of what that value is). How would you do that in vanilla Magento? Unfortunately, there is no easy way to do this. Your best approach is going to be exporting your product database to CSV (System->Import/Export->Export) and then using Excel to sort and filter to the products with a value for that attribute, as each attribute will appear in its own column in Excel. We are always looking for ways to improve processes and make things smoother. As always, there is a better way.

We created a small Magento plugin for the Catalog->Attributes->Manage Attributes panel which adds a new tab to the left side: “Products Uses”. This tab displays a grid of products which have a value for that attribute. The products can be sorted and filtered using any of the inputs across the top of the grid. For example, you could filter to view only the products have the value “red” for the attribute “color”. There is even an “edit” link which opens the back-end Magento Admin panel for the product in a new tab so edits can easily be made to the product.

Example

This little plug-in is just one of many plugins available to SwiftOtter clients. Best of all, we are releasing it today for you to use.

You can download the plugin on GitHub.

If you keep running into hassles or annoyances while managing your Magento storefront, reach out to your 2nd level Magento support agency. They may well have a tool which can help solve the problem you are facing.

Magento's WYSIWYG editor isn't particularly great and hasn’t been changed for a long, long time. However, in order to ensure that non-technical content managers can carry out their work effectively, it is important that it is integrated correctly with the site's theme. Most themes have custom CSS functionality that can be used on the site.

There are generally two ways to facilitate the communication of their use to content managers. One is to focus on the HTML classes and perhaps set up a document that would have information on how to use them on the site. The other approach is to automate it by building custom modules.

This article focuses on the former: adding classes to the styles menu in the Magento WYSIWYG editor. It’s an easy solution and doesn't take more than an after plugin to add options to the styles menu. The goal of this ost is to provide details on what is required to add the options.

For a succinct tutorial on how to add styles to the WYSIWYG, go the end of the article.

Add classes to WYSIWYG editor Styles menu:

You will need to create a module first for handling this. In the etc/ folder of your module, create a di.xml file. The di.xml is where you will specify the class in your module that will be a plugin to the core class. For this, you will use an after Plugin. This plugin will be for the Magento\Cms\Model\Wysiwyg\Config class which outputs configuration that the WYSIWYG editor uses to display. The di.xml should look like the following example (where the module namespace is SwiftOtter\Editor):

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="\Magento\Cms\Model\Wysiwyg\Config">
        <plugin name="add_wysiwyg_data" type="\SwiftOtter\Editor\Plugin\WysiwygConfig" sortOrder="10" />
    </type>
</config>

Now create a class in the Plugin namespace of your module. While that namespace isn't required, it is a convention. That class needs to have a public function called afterGetConfig(). Because the function starts with after, Magento will know that this plugin is to be run after the original method. The GetConfig part is the name of the method inside of the base class: Magento\Cms\Model\Wysiwyg\Config::getConfig(). This function should receive two parameters: $subject and $config. The $config is the return value of the base method. At its simplest, this is all we need in order to be able to add some extra data to it. Because $config is a \Magento\Framework\DataObject, I suggest type hinting that as well.

Inside the method, we will add data to the config. The style options need to be provided through the theme_advanced_styles property. If you want to dig into the editor a little more, the best place to start is the setup.js file which can be found here: /lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js. The theme_advanced_styles value expects a specific format: "Hero=hero;Hero Image=hero__image;". That will display two options within the styles menu: Hero and Hero Image. When adding data to $config with the addData() method, you will use an array like this where the value is a key that the Javascript class expects: ['theme_advanced_styles' => 'valueString...'].

However, there is a small catch here due to a nuance with the Javascript class for the WYSIWYG editor. You need this array to be the value of another array that has a key of 'settings'. This is because of the way that the options are loaded into the Javascript class. The values of the settings object extend the default configuration options. As a result, the parameter within addData() should look like this: ['settings' => ['theme_advanced_styles' => 'valueString...']].

To make it easier to add options and understand what is currently available, I chose to use an associative array as the basis for the additional configuration. This is not necessary, but I find it much easier to work with than the awkward protocol for the styles. I used a concise array_map() to compile the associative array into an indexed array with the key and value separated with =:

$styles = array_map(function($title, $class) {
    return "{$title}={$class}";
}, array_keys($styleArray), array_values($styleArray));

With those things put together, the method call that actually adds the data looks like this:

$config->addData(["settings" => ["theme_advanced_styles" => implode("; ", $styles)]]);

Don't forget to return the updated $config object. A return value is actually required for after plugins. After that, clear the cache, and you should have options display under the "Styles" menu in the WYSIWYG editor. As a result, content managers will be able to easily add custom styles to markup.

Areas for improvements

While there are many areas in the Magento 2 editor that could be improved, one is that this doesn't allow you to add containers or control which elements it should be applied to. For example, if a user selects all the elements in a list (<ul><li /><li /></ul>), it will add the selected class to each individual list item. I would prefer to have a degree of control over whether it is applied to the <ul /> or even a surrounding <div /> added with that class.

The other thing that would be good to do is move the associative array that provides the classes out into a separate file—like a model.

Short Tutorial:

Step #1: Inside a module, add a plugin for the \Magento\Cms\Model\Wysiwyg\Config class:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="\Magento\Cms\Model\Wysiwyg\Config">
        <plugin name="add_wysiwyg_data" type="\SwiftOtter\Editor\Plugin\WysiwygConfig" sortOrder="10" />
    </type>
</config>

Step #2: Create a plugin class that will add data to the configuration data inside the file app/code/SwiftOtter/Editor/Plugin/WysiwygConfig.php:


namespace SwiftOtter\Editor\Plugin;

class WysiwygConfig {
    public function afterGetConfig($subject, \Magento\Framework\DataObject $config)
    {
        $styleArray = [
            'Hero - Image' => 'hero__image',
            'Hero - Heading' => 'hero__heading',
            'Hero - Action Button' => 'hero__primary-action'
        ];

        $styles = array_map(function($title, $class) {
            return "{$title}={$class}; ";
        }, array_keys($styleArray), array_values($styleArray));

        $config->addData(["settings" => ["theme_advanced_styles" => implode("; ", $styles)]]);

        return $config;
    }
}

Step #3: Clear cache and refresh admin page.

Someone on Twitter recently mentioned the push back he saw from developers in regards to using an SVG icon system. Since I had just built an SVG icon system for a client's site re-design, I decided to take this opportunity and talk about the good and bad that I noticed in using entirely SVGs on a Magento store.

In our case, icons are being used across two websites and multiple locales. The site is built on Magento 1.9, although that doesn't really matter to the scope of this post. The two sites shared a base theme and then had site and locale-specific themes that overrode where necessary. After building out the icon system and using it on the site, I have come to see many benefits of using SVG over other options—primarily font icons—but there are certainly a few hurdles to cross.

I will start by covering the benefits of using SVG on this site, then the drawbacks, along with how we solved some of the problems. I will finish with information on the icon system itself and how it works. I consider myself somewhat new to some of this so please sound off in the comments if I am missing something or there's a better way!

Benefits:

Since there are many other articles detailing the benefits of using SVGs instead of icon fonts, I want to focus on the reasons why SVG worked well on the site we were developing.

Modularity:

Instead of having one large icon file, we split them out into several based on the category, and position, or importance, of the icon. This way, if a certain page didn't need any icons from a group, it wouldn't have to request that file. Also, if one of the sites had more, or a different set of icons in a category, it would not impact the set on the others. The bottom line is we send a near-minimum of icons to the user.

Maintainability:

Things change. With the SVG icon system, editing one icon is as simple as opening the source SVG and making the adjustment. It's extremely easy.

Icons can also be added to and removed from the system just by adding or removing individual SVG files from the respective folders. As a result, if an icon becomes deprecated, it can be removed by simply deleting a single file.

CSS Control

From the CSS, we control the stroke (color), stroke-width, and fill. In addition to that, it is even possible to set two different values for colors on separate elements within the icon. This is accomplished by setting the property in the SVG to have a value of currentColor. For example, in the following real example, notice the control that we have over the colors. When the .is-open class is applied (via Javascript), the icon completely transforms (inverts) colors.

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="17" viewBox="0 0 24 17">
  <g fill-rule="evenodd" transform="translate(0 1)">
    <rect width="24" height="3" y="1.5" />
    <rect width="24" height="3" y="10.5" />
    <circle cx="18" cy="3" r="3" fill="currentColor"/>
    <circle cx="7.5" cy="12" r="3" fill="currentColor" />
  </g>
</svg>

SCSS (slightly adjusted for clarity):

.filter-icon {
  color: $white;
  fill: $grid-action-color;
  stroke: $grid-action-color;

  .is-open & {
    color: $grid-action-color;
    fill: $white;
    stroke: $white;
  }
}

No Fallbacks

I'm not sure if this actually a benefit, but it had been decided that we did not need to support Internet Explorer 8. As a result, an entire component of complexity of handling fallbacks was gone. Also lost with that was a benefit that icon fonts would have had.

Drawbacks

These drawbacks proved to be inconveniences: not real problems that couldn't be worked with.

No default width/height per icon:

Perhaps our biggest problem was the fact that we could not set a default icon size for the SVGs in the source SVG itself since they were external sprites. I have not found a way to transfer the size set in the external SVG sprite to something the browser would observe when using that sprite.

The default recommendation online is to declare the width and height attributes on the <svg> in the browser, but that can prove inconvenient since they would need to be looked up from the source—especially if the icon is not square. The other option is to declare them in CSS, but that would then override the width and height attributes.

What we did was stay with the width and height attributes. For icons used in .phtml templates, this was quite simple because the SVGs were easy to look up. For icons used in the Magento CMS, we created a widget to render the icon. The width and height still need to be declared, but most of the icons in use there are square. Also, if the icon is square, only the width needs to be declared, and the widget will copy the value to the height.

Summary: no perfect solution.

Use Syntax is verbose:

The entire <use> syntax is quite long and unnatural for content authors that would only be familiar with more basic HTML. In our case, we used a widget to render it in the Magento CMS. As a result, it doesn't take much at all to render an icon.

For the templates, we used a Magento Helper class to make the markup more concise. The helper passes the necessary arguments through to the same template used for the widget.

Summary: after the initial setup, this worked great.

Use of SVGs in CSS pseudo elements:

There are instances where CSS pseudo-elements are simply better than including the icon directly in the DOM. Since we can't (quite) use SVG sprites in CSS, we have to reference the external SVG itself. What we did was create a separate folder for SVGs that were to be used in this way. Gulp would then minify the SVGs and put them into a different folder where they could be used. We had to declare colors directly in the SVG itself as well so it worked well to have them separate anyway. The number of icons used in this way is actually quite small so this hasn't been a problem.

Summary: not an ideal, but still feasible workaround.

System

We use Gulp for building the front end assets. The gulp-svg-sprite allowed us to have fine-grained control over the output of the SVG files.

Process

As I mentioned earlier, there are several different categories of icons used on the site. Each category is a folder of SVG files. It would be like this:

/site
    /checkmark.svg
    /close.svg
    /download.svg
    /plus.svg

When the Gulp task runs, it will create an SVG file named site.svg. Inside of that file are many <symbol> elements that are named based on the files: checkmark, close, download, plus. The icons will be displayed with HTML like this:

<svg width="20" height="23" class="icon-checkmark">
    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/skin/frontend/theme/default/images/dist/site.svg#checkmark"></use>
</svg>

To add an additional icon to the site category is as simple as dropping an SVG into that folder. Gulp watches it and will re-compile the output SVG.

Directory structure

The folder structure would be something like the following. In a single theme, it would look like this, inside the skin/images folder:

/svg
    /site
        /checkmark.svg
        /close.svg
        /download.svg
        /plus.svg
    /objects
        /people.svg
        /gym.svg

Gulp task

Now some code! Let's take a look at the Gulp task that handles the icon system. To start with, we will go through the main task in its (near) entirety. Note that there are a number of config objects above this, but I think the naming should make it somewhat self-explanatory. Also, keep in mind that this is more complicated than it otherwise would be because of the Magento theme folder structure.

gulp.task('svg', function() {
    var themes = themeArray(),
        tasks = themes.map(function(theme) {
            var spriteFolders = getFolders(theme + paths.images.spriteFolders, true);

            return spriteFolders.map(function(sprite) {
                var svgConfig = clone(config.svg);
                svgConfig.mode.symbol.sprite = path.join(theme, paths.images.dist, sprite + '.svg');

                return gulp.src([
                        path.join(theme, paths.images.spriteFolders, sprite, '*.svg'),
                        paths.images.alwaysInclude + sprite + "/*.svg"
                    ]).on('end', () => { gutil.log(`Compiling ${gutil.colors.magenta(sprite + '.svg')}`); })
                    .pipe(svgSprite(svgConfig).on('error', error => { logError('SVG Sprite Error', error);}))
                    .pipe(rename(sprite + '.svg'))
                    .pipe(gulp.dest(path.join(theme, paths.images.dist)));
            });
        });

    return merge(tasks);
});

When the SVG task runs, it loops through an array of paths to the various themes in use on the website. They are returned from the themeArray() variable. Inside the single theme directory, a list of the folders (categories) are obtained.

Here is the getFolder function:

function getFolders(dir) {
    try {
        return fs.readdirSync(dir)
            .filter(file => {
                return fs.statSync(path.join(dir, file)).isDirectory();
            });
    } catch (e) {
        return [];
    }
}

Inside each folder (SVG group), a copy of the base svg-sprite configuration is created and the sprite property is modified to the current group's name. After the configuration is modified, there are some familiar Gulp lines. In gulp.src, the second array item is the global theme's sprites. This allows us to have many of the icons only in the global theme while the child themes override or change if necessary.

The actual compilation happens in a simple-looking call to the gulp-svg-sprite plugin: svgSprite(svgConfig). The sprite file is then named based off of the group folder and saved.

Finally, merge is a call to the merge-stream plugin where all the Gulp tasks in the loop actually get run.

Gulp, SVGs, and bumps

That's how we did the SVG icon system. As more browsers support SVG fragments, I suspect that will quickly become a superior approach. For the time being, these type of solutions keep pushing SVG to the forefront of the web, and we are pleased with the results.

So what do you think?