Testing Magento 2.3~2.4 on Windows: Making the admin panel work

The official documentation from Adobe doesn’t clearly state that Magento 2 can be installed on a Windows environment; however many tutorials found on the Internet still demo Magento 2 on a Windows environment.

Developers with no experience on Linux might have difficulties when trying to evaluate Magento 2 for the first time, so it is understandable to try an installation on a Windows environment to start taking their first steps into Magento 2.

This document assumes that Magento 2.3.x or 2.4.x is already installed in a full Windows environment (no VM or docker involved), so the installation process will be skipped in this article.

Since Windows is not the recommended environment for installing Magento, problems after a fresh install might be common. The first issue you would face after installing Magento 2.3.x or 2.4.x is the admin site display.

Ooops…. Now what?

When exploring the page via Developer Tools it is clear that something went wrong when loading the page scripts and theme.

The solution for this is tricky and the errors don’t tell that much for troubleshooting; however, remember that the recommended environment for installing Magento 2 is Linux; and that might be a good indication on how to deal with the problem.

One of the files in charge of loading the theme is Validator.php, located at vendor\magento\framework\View\Element\Template\File; inside there is the following function that validates if the current Magento path is related to the directory.

protected function isPathInDirectories($path, $directories)

 {

        if (!is_array($directories)) {

            $directories = (array)$directories;

        }

        $realPath = $this->fileDriver->getRealPath($path);

        foreach ($directories as $directory) {

            if (0 === strpos($realPath, $directory)) {

                return true;

            }

        }

        return false;

 }

Inside the foreach loop let’s compare the params $realPath and $directory passed to the strpos function:

echo $realPath;

installation\path\vendor\magento\module-backend\view\adminhtml\templates\page\js\require_js.phtml

echo $directory;

installation/path//var/view_preprocessed/pub/stat/

Did you notice? The slashes are in different directions, causing the isPathInDirectories to return false all the time.

The reason for this is that getRealPath will return the correct slash format for the current OS, being the backslashes the format for Windows file paths, while $directory is just considering the Linux convention; so the solution would be to use the same getRealPath function for converting the $directory variable into the correct Windows file format.

protected function isPathInDirectories($path, $directories)

{

     if (!is_array($directories)) {

     $directories = (array)$directories;

}

$realPath = $this->fileDriver->getRealPath($path);

foreach ($directories as $directory) {

    $directory = $this->fileDriver->getRealPath($directory);

    if (0 === strpos($realPath, $directory)) {

        return true;

    }

}

return false;

}

After this quick fix you can just refresh the browser for normally displaying the admin login page.

However, please be noted that the solution proposed here is just testing purposes. If you want to make the changes permanently you must follow the Magento developer standards for developing a plugin or preference.

Content Security Policies on Magento: Fixing Yotpo policies errors

Yotpo is a useful platform built-in into Magento that helps merchants to easily collect and display customer reviews, photos and videos, improving customers’ trust; therefore boosting sales.

Although, Yotpo is fully integrated and configured to work with Magento it is not error free after a clean installation; being the most common issue related to Content Security Policies (CSP).

CSP are a security tool to mitigate site attacks such as: card skimmers, session hijacking, clickjacking, and more. Requiring the server to previously whitelist in the HTTP headers the external sources for scripts, styles and other resources.

When a SCP security issue occurs a developer might see an error message appearing the browser developer console, being very similar to the following one:

[Report Only] Refused to load the script ‘https://xxxx’ because it violates the following Content Security Policy directive: “xxxx”.

For fixing this error, the first thing we need to do is to add a etc/config.xml and configure the SCP endpoints for admin and storefront.

<?xml version=”1.0″?>

<config xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”urn:magento:module:Magento_Store:etc/config.xsd”>

   <default>

       <csp>

           <mode>

               <storefront>

                   <report_only>1</report_only>

               </storefront>

               <admin>

                   <report_only>1</report_only>

               </admin>

           </mode>

       </csp>

   </default>

</config>

Next, we need to create etc/csp_whitelist.xml that will have the main configuration to the whitelist resources for the different CSP policies. Usually, for Yotpo make sure to add all the following policies:

<?xml version=”1.0″?>

<csp_whitelist xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd”>

   <policies>

       <policy id=”script-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”connect-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”frame-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”form-action”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”img-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”style-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

           </values>

       </policy>

       <policy id=”font-src”>

           <values>

               <value id=”yotpo_main” type=”host”>yotpo.com</value>

               <value id=”yotpo_main_www” type=”host”>www.yotpo.com</value>

               <value id=”yotpo_p” type=”host”>p.yotpo.com</value>

               <value id=”yotpo_staticw2″ type=”host”>staticw2.yotpo.com</value>

               <value id=”yotpo_w2″ type=”host”>w2.yotpo.com</value>

               <value id=”gstatic” type=”host”>*.gstatic.com</value>

           </values>

       </policy>

   </policies>

</csp_whitelist>

With this, we are expliciting whitelisting the external sites ot be allowed to provide resources to our site, helping to prevent malicious scripts from unknown sources attempting to:

  • Send credit card info to an attacker’s website.
  • Make users click on elements that are not supposed to be on page.

Do noy forget that for installed 3rd parties modules, you might need to check with the vendor/developer for getting more information on what dns you should whitelist.

Basic Elasticsearch troubleshooting

Since Magento 2.4.0, Elasticsearch has become the default indexation tool; however, some small issues might occur as well; being the most important not able to index correctly, requiring some Linux skills. If after executing a reindex you consider the performance of the indexing has been decreased this article might help you.

The first step is to check if ElasticSearch is running correctly by executing “curl -X GET” as follows:

If you don’t get the ElasticSearch information that means you are not connected to the cluster, so please check your connectivity and/or try to restart the ElasticSearch service/server.

The same curl command provides more powerful options for browsing the current indexes.

The display of the CURL GET command by the index would be very long but it will give an idea of how healthy your service/server is.

If you still consider that not all indexes are being handled correctly you can delete all indexes with the DELETE option.

The above “acknowledged” message means that all indexes have been deleted; letting you trigger a new re-index and let ElasticSearch rebuild itself.

Magento Cloud Command-Line: Basic Usage

One of the many purposes of Magento Cloud is to save valuable DevOps time and to reduce deployment issues. Although is very convenient to utilize the UI provided by Magento Cloud Web, it is even more time-efficient to do the same tasks via Command-Line (CLI).

First, the Magento Cloud CLI must be downloaded and installed into the computer client:

  1. Make sure you are logged in as the OS user that will access the cloud resources (not necessarily the root user).
  1. Install the magento-cloud CLI.

$ curl -sS https://accounts.magento.cloud/cli/installer | php


Then, add magento-cloud to the bash profile. 

$ export PATH=$PATH:$HOME/.magento-cloud/bin

After that, reload the bash profile

$ ~/.bash_profile

Now you can execute Magento clouds commands by typing magento-cloud or mgc.  Please note that when using the console for the first time, you need to login by providing your username and password. 

$ mgc


After this, when typing mgc list you will be able to check the available CLI commands; where most of them provide a basic CLI menu for guiding you into the executed procedure.

$ mgc list

After downloading it is recommended that you generate a public SSH key upload it to the project. (Generating an SSH key is out of the scope of this article)

$ mgc ssh-key:add ~/.ssh/id_rsa.pub

Note: When generating any SSH key please keep the default name as “id_rsa.pub”, because by default Magento cloud will look for this specif file name.

WEB UI actions on CLI

For the purpose of imitating the most common Web UI actions you might be interested in check the following commands (In alphabetical order):

db:dump:  Create a dump file from the environment selected and downloads it into your computer. No need to mention how convenient is this command that reduces many steps and data transfer between the host and client.

db:sql: Opens the Mysql CLI interface for the specific environment.

environment:active:  Activates the selected environment.

environment:branch: Branches an environment. Allowing you to select the parent branch.

environment:list: Lists the current environments for the project(s).

environment:merge: Merges two environments

environment:redeploy: Redeploys an environment without pushing any code. Very convenient when you just need to restart the environment.

environment:ssh: SSH to an environment.

environment:sync: Syncs an environment from its parent.

mount:download: Allows to download files from the environment.

mount:upload: Allows to upload files from the environment.

tunnel:open & tunnel:info: A combination of these commands allows you the get ip addresses, usernames, and passwords of the internal services of any specific environment, such as Elastics Search and Mysql DB, without the need to access the environment directly.

It is worth mentioning that if you would like to explore what param combinations are possible when using the above commands you can always use the “–help” param.

$ mgc –help command

In definitive, using the magento-cloud CLI is not a must when using Magento Cloud, however, when getting used to it will speed up, even more, the clouding experience.

Reducing Magento Cloud Git Repository

Magento Cloud customers and operators might not know, but Magento Cloud Git repository size on the Cloud is not limitless; and because of this, a junior Engineer might not pay attention to details when pushing new commits into this Git repository.

A common mistake would be to commit and push “media” resources or big static files to the repository, causing an exponential growth when merging between branches; leading to being unable to connect via SSH or browser to any of the environments. Also, the Git repository might not be able to allow pushing new commits.

Usually, the first attempt to check and remove any media resource or unnecessary data is to remove, make a new commit and push back to the repository. Unfortunately, this workflow never really works. 

Deleting files in a commit doesn’t actually reduce the size of the repo since the earlier commits and blobs (files) are still around in previous commits and included in the git database, so the repository will never decrease. 

With that being said, the Magento Cloud repository can be reduced by following these steps:

  1. Navigate to the repository:

cd cloud_repository/

  1. Change to the branch that contains big files:

git checkout cloud-branch

  1. Execute filter-branch to remove the bigs files and save the re-written Git history in HEAD

git filter-branch –force –tree-filter ‘rm -rf pub/media/*’ HEAD

  1. Instruct Git to purge and prune the unwanted data:

git reflog expire –expire=now –all && git gc –prune=now –aggressive

  1. Lastly, force push to the repository:

git push –force origin master

After this, the repository size will be reduced; helping to prevent any of the issues caused by the lack of space in the Git repository; however, before performing these steps make sure to make a copy of the repository in case some info needs to be restored in case of errors

It is also good to take into account that after re-writing and/or purging the Git repository all the collaborators need to rebase or re-clone the repository, in order to avoid re-pushing the old commits and blobs.