URN Schema Locations in Magento 2

acl.xml

urn:magento:framework:Acl/etc/acl.xsd

address_formats.xml

urn:magento:module:Magento_Customer:etc/address_formats.xsd

analytics.xml

urn:magento:module:Magento_Analytics:etc/analytics.xsd

cache.xml

urn:magento:framework:Cache/etc/cache.xsd

catalog_attributes.xml

urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd

communication.xml

urn:magento:framework:Communication/etc/communication.xsd

config.xml

urn:magento:module:Magento_Store:etc/config.xsd

content_type.xsd

urn:magento:module:Magento_PageBuilder:etc/content_type.xsd

Page Builder specific, used by various XMLs in magento/module-page-builder/view/adminhtml/pagebuilder/content_type

cron_groups.xml

urn:magento:module:Magento_Cron:etc/cron_groups.xsd

crontab.xml

urn:magento:module:Magento_Cron:etc/crontab.xsd

csp_whitelist.xml

urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd

data_source.xml

used by magento/module-store/etc/

db_schema.xml

urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd

default.xml

urn:magento:framework:View/Layout/etc/page_configuration.xsd

definition.map.xml

urn:magento:module:Magento_Ui:etc/ui_definition.map.xsd

definition.xml

urn:magento:module:Magento_Ui:etc/ui_definition.xsd

directory.xml

urn:magento:module:Magento_MediaGalleryApi:etc/directory.xsd

eav_attributes.xml

urn:magento:module:Magento_Eav:etc/eav_attributes.xsd

email_templates.xml

urn:magento:module:Magento_Email:etc/email_templates.xsd

error_mapping.xml

urn:magento:module:Magento_Payment:etc/error_mapping.xsd

esconfig.xml

urn:magento:module:Magento_Elasticsearch:etc/esconfig.xsd

events.xml

urn:magento:framework:Event/etc/events.xsd

export.xml

urn:magento:module:Magento_ImportExport:etc/export.xsd

extension_attributes.xml

urn:magento:framework:Api/etc/extension_attributes.xsd

fieldset.xml

urn:magento:framework:DataObject/etc/fieldset.xsd

import.xml

urn:magento:module:Magento_ImportExport:etc/import.xsd

indexer.xml

urn:magento:framework:Indexer/etc/indexer.xsd

layouts.xml

urn:magento:framework:View/PageLayout/etc/layouts.xsd

media_content.xml

urn:magento:module:Magento_MediaContentApi:etc/media_content.xsd

urn:magento:module:Magento_PageBuilder:etc/menu_section.xsd

urn:magento:module:Magento_Backend:etc/menu.xsd

module.xml

urn:magento:framework:Module/etc/module.xsd

mview.xml

urn:magento:framework:Mview/etc/mview.xsd

page_layout.xml

urn:magento:framework:View/Layout/etc/page_layout.xsd

used in view/frontend/page_layout/.*.xml

page_types.xml

urn:magento:framework:View/Layout/etc/page_types.xsd

payment.xml

urn:magento:module:Magento_Payment:etc/payment.xsd

pdf.xml

urn:magento:module:Magento_Sales:etc/pdf_file.xsd

product_options.xml  

urn:magento:module:Magento_Catalog:etc/product_options.xsd

product_types.xml

urn:magento:module:Magento_Catalog:etc/product_types.xsd

queue_publisher.xml

urn:magento:framework-message-queue:etc/publisher.xsd

queue_topology.xml

urn:magento:framework-message-queue:etc/topology.xsd

queue.xml

urn:magento:framework-message-queue:etc/queue.xsd

reports.xml

urn:magento:module:Magento_Analytics:etc/reports.xsd

resources.xml

urn:magento:framework:App/etc/resources.xsd

routes.xml

urn:magento:framework:App/etc/routes.xsd

sales.xml

urn:magento:module:Magento_Sales:etc/sales.xsd

search_engine.xml

urn:magento:framework:Search/etc/search_engine.xsd

search_request.xml

urn:magento:framework:Search/etc/search_request.xsd

sections.xml

urn:magento:module:Magento_Customer:etc/sections.xsd

system.xml

urn:magento:module:Magento_Config:etc/system_file.xsd

validation.xml

urn:magento:framework:Validator/etc/validation.xsd

view.xml

urn:magento:framework:Config/etc/view.xsd

webapi_async.xml

urn:magento:module:Magento_WebapiAsync:etc/webapi_async.xsd

webapi.xml

urn:magento:module:Magento_Webapi:etc/webapi.xsd

widget.xml

urn:magento:module:Magento_Widget:etc/widget.xsd

zip_codes.xml

urn:magento:module:Magento_Directory:etc/zip_codes.xsd

Adobe just released Emergency Patch for exploited 0-Day

CVE-2022-24086 – Arbitrary Code Execution on Magento and Adobe Commerce

Affected versions: 2.3.3-p1-2.3.7-p2 and 2.4.0-2.4.3-p1

Get your live environments updated ASAP.

Patch: https://support.magento.com/hc/en-us/articles/4426353041293-Security-updates-available-for-Adobe-Commerce-APSB22-12-

Description of the issue: https://helpx.adobe.com/security/products/magento/apsb22-12.html

I’m going to walk through the concept of Mage-OS extensively, covering everything that is up there as of today (Feb 5/22).

My goal is for you to get a complete grasp of this concept once you read this post, even if at this moment you have no idea what it is.

This post will consist of the information extracted from the three official posts from the Mage-OS community, namely being:

But it won’t be that long.

I’ll use a problem-solving process to describe what was already concluded. Everything will be coherent and substantive. I will let myself skip all of the populist statements as it would make everything here unnecessarily long.

The problem-solving process consists of 5 steps:
(1) Defining the problem
(2) Analyzing the problem
(3) Identifying and evaluating possible solutions
(4) Selecting and justifying the optimal solutions
(5) Implementation and review

Mage-OS folks defined the problem through a public letter that was published in September 2021. Everyone interested could publicly sign it, and it ended with 1640 people signing it up. The original link is here.

(1) Defining the problem

Magento community is feeling abandoned and ignored by Adobe, worrying about the future of the Magento Open Source.

There are a few reasons why:

(2) Analyzing the problem

  1. Adobe Commerce is moving towards composable microservices that are cloud-hosted, and suitable only for the largest merchants. This topic was briefly covered by Igor Miniailo and Nishant Kapoor in Extending Magento Commerce with Adobe I/O talk. This basically means the decomposition of the PHP Monolith.
  2. Community who believe the monolith is a valid approach feels uneasy about the future of Magento
  3. No clear direction on the Open Source product compared to the amount of communication and marketing around Adobe Commerce
  4. A growing disconnect between Adobe and the Community as a result of dissatisfaction around the Open Source contribution model, lack of communication, and lack of a clear roadmap
  5. Leadership at Adobe changed and core members with strategic positions left. There was no replacement for these figures and a lack of business case for further investment into Open Source

And it looks like Mage-OS wants to solve those issues by:

(3) Identifying and evaluating possible solutions

  1. community-driven fork that will be upstream-compatible with Magento Open Source to secure long term viability for all the businesses depending on it
  2. creating a movement through open letter to the Magento Community, so it brings Adobe’s decision makers to the table

No other possible solutions were mentioned. The are multiple reasons why Mage-OS is thinking those two are a good idea:

(4) Selecting and justifying the optimal solutions

  1. when the monolith is ultimately deprecated, all companies who want to remain on the monolith platform will be able to do so
  2. its going to be a collaborative effort, ideally together with the Magento Association, and (through them) with Adobe
  3. keep Magento alive, and give it a bright future by putting the focus on merchants, both big and small
  4. compatible with all existing Magento 2 extensions and integrations
  5. more accessible to developers
  6. composed of a default lightweight package selection
  7. faster integration of PRs
  8. simple migration to Magento Open Source and Adobe Commerce

Benefits for Merchants

  1. simpler and more accessible to a majority of developers
  2. a growth path from the small beginning up to Adobe Commerce and Experience Manager
  3. reduced hosting costs due to simpler environmental requirements
  4. access to the big pool of Integrations and extensions for Magento and Adobe Commerce

Benefits for Agencies

  1. reduced build time and less complexity to optimize your project costs
  2. easier to onboard new talent thanks to a more accessible codebase
  3. ability to target the lower SMB market with attractive margins thanks to lower project cost
  4. developers happiness: less frustration, more wins to motivate your teams and boost your talent retention

Benefits for Technology Partners

  1. business growth as the Magento market share increases
  2. access to the lower end SMB market section of merchants and agencies
  3. bigger ROI for existing products due to a longer Magento lifespan

Benefits for Developers

  1. improved sense of ownership
  2. faster moving distribution
  3. easier to contribute
  4. simpler to set up a development environment
  5. good entry point for a developer career in the Magento / Adobe Commerce ecosystem
  6. Bigger ROI for acquired Magento development knowledge due to a longer Magento lifespan

Benefits for Adobe

  1. reduced pressure on the Magento Open Source GitHub PR/Issue list
  2. create a protective buffer between community contributions and Adobe Commerce to safeguard stability
  3. community contributions can be evaluated in production environments without the risk of introducing instability to Magento Open Source or, by extension, Adobe Commerce
  4. funnel for merchants exhibiting the biggest growth from Mage-OS into Adobe Commerce and Adobe Experience Manager
  5. protect and grow the lower-end Magento SMB market market share
  6. competitive advantage vs other eCommerce platforms due to a vibrant and active community

This was all in September 2021. Fast-forward February 2022, and I can safely confirm this effort was not in vain and resulted in:

(5) Implementation and review

Adobe noticing the community:

  • still cared
  • had a real need for an open strategy around open source and a commitment to the long-term support of Magento Open Source

Result: the Open Source Task Force was created by the Magento Association.

The community and Adobe get together to:

  • voice these concerns
  • discuss the future of Magento
  • find common ground on important issues

It is all to fix the relationship between Adobe and the Community.

Mage-OS has a one-hour meeting once every second week. Results are mixed, and eight calls have taken place until now, joined by about 10 people.

Almost half are Adobe employees, and the rest consists of community members, including three Mage-OS members.

So far Mage-OS got an alignment on:

  • what the community needs from Adobe
  • what Adobe is willing to provide

And also commitment has been made by Adobe to:

  • keep investing into Magento Open Source
  • ensure us there is no plan for deprecation of the monolith

Important takeaways:

  1. Adobe has proposed a Community maintained “Short Term Support” (STS) version of Magento Open Source
  2. Awareness of the Community and the importance of Magento Open Source is growing inside Adobe
  3. Luma will be deprecated, but not on short term
  4. possibility of slimming down the core of the Magento Framework to allow multiple distributions in the future
  5. Adobe will not provide any marketing around Magento Open Source towards merchants from either magento.com or the adobe.com website
  6. magento.com domain now redirects to a landing page at adobe.com with resources around Magento
  7. Adobe will take care of the basic needs to be able to keep using Magento Open Source for the next couple of years
  8. full vendor support for critical bugs, stability upgrades and security support
  9. the power of the community and the backing of a large enterprise will enable innovation and growth of the Magento eco-system for many years into the future
  10. Adobe wants to build integrations for other Adobe products and microservices, rather than building new features using Magento (PHP) extensions
  11. to make platform and community flourish and grow, it will have to come from the community
  12. The fork was created, and its tracking changes on the official Magento repositories
  13. setup of the infrastructure and processes to make Mage-OS a sustainable long-term project
  14. fork is tracking changes on the official Magento repositories and automatically updates, then creates meta-packages from the repository and puts these into a distributed repository that can be used with Composer
  15. fork, build scripts, and all documentation will be published soon
  16. Mage-OS goal is full transparency
  17. processes will be up for discussion on Discord
  18. The Fork is a contingency fallback before large deprecations that might happen on the official Magento repository, or its entire removal
  19. through fork Mage-OS wants to have complete ownership over a copy of the product and have the knowledge to maintain such an infrastructure
  20. optional functionalities in core such as a choice between MySQL search or Elasticsearch, or removing MSI (Multi Stock Inventory) will be in fork by default
  21. implementation of bug fixes will be faster
  22. Implementation of performance improvements faster
  23. fork will Include new features, and repackage Luma into an LTS version, removing Luma from core by default
  24. Magento events needs to make a comeback, to educate and welcome newcomers, celebrate our victories and help each other out through sharing solutions to our common problems as we’ve done for over a decade
  25. Mage-OS will open up to the public using Discord.com as a communication platform, where we would like to welcome you and exchange ideas on how to start defining the future of Magento
  26. access information to this environment will be published on website and newsletter as soon as it’s made it available

It is clear that Adobe will not invest heavily in any innovation or feature development on Magento.

The Mage-OS initiative has been started by the following companies and individuals:

  • Willem Wigman, Hyvä Themes B.V.
  • Vinai Kopp, Hyvä Themes B.V.
  • Andreas von Studnitz, integer_net GmbH
  • Fabian Schmengler, integer_net GmbH
  • Ignacio Riesco, Interactiv4 S.L.
  • Óscar Recio, Interactiv4 S.L.
  • Peter Jaap Blaakmeer, elgentos
  • Wouter Steenmeijer, elgentos
  • Jeroen Boersma, elgentos
  • Kuba Zwolinski, snow.dog
  • Kamil Balwierz, snow.dog
  • Bartek Igielski, snow.dog
  • Simon Sprankel, CustomGento GmbH
  • Ryan Hoerr, ParadoxLabs, Inc
  • Thien-Lan Weber, OneStepCheckout
  • Alessandro Ronchi, Magento Community Maintainer
  • John Hughes, Fisheye Media Ltd.
  • Tomas Gerulaitis

Conclusion

I’d like to personally thank all of the initiators of this movement, as the stagnated Adobe’s outlook to Open Source led to way too many unnecessary frustrations. Once discord will become open I’ll gladly join in to help you.

Mage-OS is a movement that will probably blaze the trail of Magento’s future for the next couple of years.

PR

In PHPStorm navigate to Run -> Edit Configurations… and add a new PHP Script.

As a File use your project’s path to bin/magento
As Arguments input command you wish to debug, for example: setup:upgrade --keep-generated
Lastly, set your IDE key through environment variables: XDEBUG_CONFIG="idekey=PHPSTORM"

Do note that it won’t work if Xdebug is disabled or is not installed.
Check if it’s on by running php -v.
If it’s enabled, you will see something like this:

PHP 7.4.27 (cli) (built: Dec 20 2021 21:28:15) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.27, Copyright (c), by Zend Technologies
    with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans

If it’s disabled, the Xdebug line won’t be there:

PHP 7.4.27 (cli) (built: Dec 20 2021 21:28:15) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.27, Copyright (c), by Zend Technologies

sudo phpenmod xdebug to enable it
sudo phpdismod xdebug to disable it

You should also restart your webserver:
sudo service apache2 restart if you are using Apache
sudo service nginx restart if you are using Nginx

I moved those enabling/disabling steps into .bashrc file to make toggling between Xdebug a little bit faster. You can find it here.

Full working example:

Click to zoom

You can test it by setting up a breakpoint here.

Run it by navigating to Run -> Run… and selecting the name that you used when setting everything up (in my case this is bin/magento setup:upgrade)

Step 1 – run PHP Script

Click to zoom

Step 2 – console tab appears

Click to zoom

Voilà! Xdebug breakpoint got triggered

Click to zoom

Happy debugging adventurer.

PHPStorm is shipped with a thing called Built-In Server which is an API that you can use to open any file in your project through IDE.

There are two prerequisites to make it work:
(1) PHPStorm project must be opened locally
(2) requested file must exist

Base URL looks like this: http://localhost:63342/api/file/

You can append any path that exists in your project to open it locally. For example, let’s say you want to open Magento_Catalog’s registration.php file:
vendor/magento/module-catalog/registration.php

The link is going to look like this:
http://localhost:63342/api/file/vendor/magento/module-catalog/registration.php

And you can test it for yourself here.

You are probably going to see this warning:

Decide by yourself if I’m trustworthy enough.

And if you want to disable those warnings permanently, go to PHPStorm settings: Build, Execution, Deployment -> Debugger and check Allow unsigned requests and press OK.

Backend

It has three tiers – Professional (easiest), Expert, and Master (hardest)

AD0-E711 Adobe Commerce Developer Professional

This exam is for a Magento developer who has learned the Magento framework at the basic level covered by the Magento 2 Development Essentials training. Candidates should have experience with PHP, MySQL, Apache/Nginx, and Linux. Those who pass this exam will earn the Adobe Certified Professional—Magento Commerce Developer credential.

Exam Guide: AD0-E711 Adobe Certified Professional – Commerce Developer Exam Guide

AD0-E709 Adobe Commerce Developer Expert

This exam is for a Magento developer with a recommended experience level of 1.5 years in customizing different areas of the Magento platform. By passing this exam the developer earns an Adobe Certified Expert – Magento Commerce Developer certification.

Exam Guide: AD0-E709 Adobe Certified Expert Magento Commerce Developer Exam Guide

AD0-E704 Adobe Commerce Architect Master

This exam is for a senior Magento 2 developer/architect with 2 years of experience in customizing different areas of Magento Commerce, leading teams of Magento developers, leading projects, making key technical decisions on a Magento project, and working with customers to build project requirements.

Exam Guide: AD0-E704 Adobe Certified Master Magento Commerce Architect

Frontend

AD0-E705 Adobe Commerce JavaScript Developer Expert

This exam is for a JavaScript developer with a broad experience in customizing Magento JavaScript for at least 1 year.

Exam Guide: Adobe AD0-E705 Adobe Certified Expert-Magento Commerce JavaScript Developer

AD0-E710 Adobe Commerce Front-End Developer Expert

This exam is for a Magento 2 Front End Developer with a deep understanding of Magento 2 fundamentals and with 1.5 years or more of experience with Magento 2.

Exam Guide: AD0-E710 Adobe Certified Expert Magento Commerce Front-end Developer Exam Guide

Miscellaneous

AD0-E706 Adobe Commerce Cloud Developer Expert

This exam is for a Magento 2 developer/architect with 6 to 12 months of experience developing for Magento Commerce Cloud. Ideally the person taking this exam will have worked on at least two Magento Commerce Cloud implementations.

Exam Guide: ACE: Adobe Commerce Cloud Developer Expert Certification

NOTE: Cloud Developer credential will be retired on March 31, 2022.
Source: Adobe ID

Click to zoom

AD0-E708 Adobe Commerce Business Practitioner Expert

This exam is designed for someone with:

  • Solid product knowledge of Magento 2 Open Source and Magento 2 Commerce including systems functionality, basics of system architecture, an understanding of integration, and the most popular third-party modules included in the standard Magento 2 installation.
  • Knowledge of basic ecommerce terms and a good understanding of typical business usage scenarios and trends.
  • Basic understanding of networking, cloud, and server infrastructure.
  • Basic understanding of the laws concerning privacy and online trading in the US and Europe, as well as other major trading nations.

Exam guide: AD0-E708 Adobe Certified Expert Magento Commerce Business Practitioner Exam Guide

Yes.

Recertifications are now required. There is a 2-year expiration on all Adobe certifications. Adobe will charge $150 (worldwide) and $80 (India) to recertify. These exams will be shorter than the original certifications.

~SwiftOtter

Existing certification holders can see their expiration dates within the Adobe Credential Manager.

Adobe will remind certification holders via email at 90, 60, and 30 days before their certification expires (this schedule is subject to change). Please keep your email updated within your Adobe Credential Manager account.

If you are an existing Magento credential holder, you will be required to recertify according to the following schedule:

Certification Achievement DateRecertification Due Date
Before December 31, 2019December 31, 2022
Between January 1, 2020, and June 30, 2021June 30, 2023

Read more: Changes to all Commerce Credentials

Adobe Commerce Recertification FAQs: Adobe Commerce Recertification FAQs

Dear Friend and Visitor,

Today I’m going to show you how to create (and easily extend) a custom Store Configuration that will allow you to save a table with selects and multiple rows into a single core_config_data row in the database.

You probably felt somewhere across your path a need for a dynamic and easily extensible config field.

Magento isn’t too helpful with the default types (i.e text, date, etc), but fortunately, they prepared a workaround via the Magento_Config module.

Desired Result we are looking for is following:

Desired Result

Click here to zoom.

To render this table we will use Magento_Config’s AbstractFieldArray by extending:

\Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray:class

Step 1: Create new module

app/code/PeterRusin/DynamicField/registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'PeterRusin_DynamicField',
    __DIR__
);

app/code/PeterRusin/DynamicField/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="PeterRusin_DynamicField"/>
</config>

Step 2: Create DynamicField class

app/code/PeterRusin/DynamicField/Block/Adminhtml/System/Config/Form/Field/DynamicField.php

<?php
declare(strict_types=1);

namespace PeterRusin\DynamicField\Block\Adminhtml\System\Config\Form\Field;

use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;

class DynamicField extends AbstractFieldArray
{
    protected function _prepareToRender()
    {
        $this->addColumn('comment', [
            'label' => __('Comment'),
            'class' => 'required-entry',
            'style' => 'width:75px'
        ]);

        $this->_addAfter = false;
        $this->_addButtonLabel = (string)__('Add Todo Comment');
    }
}

Step 3: Add DynamicField to system.xml

app/code/PeterRusin/DynamicField/etc/adminhtml/system.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="peterrusin" sortOrder="200">
            <label>Peter Rusin</label>
        </tab>
        <section id="peterrusin_dynamicfield" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"
                 showInStore="1">
            <label>Dynamic Field</label>
            <tab>peterrusin</tab>
            <resource>PeterRusin_DynamicField::system_config</resource>
            <group id="general" translate="label" type="text" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General Settings</label>
                <field id="dynamicfield_example" type="select" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Dynamic Field Example</label>
                    <frontend_model>PeterRusin\DynamicField\Block\Adminhtml\System\Config\Form\Field\DynamicField</frontend_model>
                    <backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>

app/code/PeterRusin/DynamicField/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="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="PeterRusin_DynamicField::system_config" title="PeterRusin_DynamicField Configuration" sortOrder="10" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

So far we’ve only added one column, and the table looks like this:

Comment Column

Click here to zoom.

Step 4: Add Option Source for our select

app/code/PeterRusin/DynamicField/Model/Config/Source/Todo.php

<?php
declare(strict_types=1);

namespace PeterRusin\DynamicField\Model\Config\Source;

use GuzzleHttp\ClientFactory;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\ResponseFactory;
use Magento\Framework\Data\OptionSourceInterface;
use Magento\Framework\Serialize\SerializerInterface;

/** @link https://devdocs.magento.com/guides/v2.4/ext-best-practices/tutorials/create-integration-with-api.html */
class Todo implements OptionSourceInterface
{
    const API_REQUEST_URI = 'https://jsonplaceholder.typicode.com';

    /** @var ClientFactory */
    private $clientFactory;
    /** @var ResponseFactory */
    private $responseFactory;
    /** @var SerializerInterface */
    private $serializer;

    public function __construct(
        ClientFactory $clientFactory,
        ResponseFactory $responseFactory,
        SerializerInterface $serializer
    ) {
        $this->clientFactory = $clientFactory;
        $this->responseFactory = $responseFactory;
        $this->serializer = $serializer;
    }

    public function toOptionArray()
    {
        $client = $this->clientFactory->create(
            [
                'config' => [
                    'base_uri' => self::API_REQUEST_URI
                ]
            ]
        );

        try {
            $response = $client->request('GET', 'todos');
        } catch (GuzzleException $exception) {
            $response = $this->responseFactory->create([
                'status' => $exception->getCode(),
                'reason' => $exception->getMessage(),
                'body' => '[]'
            ]);
        }
        $responseBody = $response->getBody();
        $responseContent = $responseBody->getContents();

        return array_map(
            function ($todo) {
                return [
                    'label' => $todo['title'],
                    'value' => $todo['id']
                ];
            },
            $this->serializer->unserialize($responseContent)
        );
    }
}

Step 5: Add our select to the Dynamic Field

app/code/PeterRusin/DynamicField/Block/Adminhtml/System/Config/Form/Field/DynamicField.php

<?php
declare(strict_types=1);

namespace PeterRusin\DynamicField\Block\Adminhtml\System\Config\Form\Field;

use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
use Magento\Framework\Data\OptionSourceInterface;
use Magento\Framework\DataObject;
use Magento\Framework\View\Helper\SecureHtmlRenderer;
use PeterRusin\DynamicField\Model\Config\Source\Todo;

class DynamicField extends AbstractFieldArray
{
    /** @var Todo */
    private $todoOptions;

    public function __construct(
        Context $context,
        Todo $todoOptions,
        array $data = [],
        ?SecureHtmlRenderer $secureRenderer = null
    ) {
        parent::__construct($context, $data, $secureRenderer);
        $this->todoOptions = $todoOptions;
    }

    private function markOptionAsSelected(
        DataObject $row,
        string $optionKey,
        OptionSourceInterface $optionSource,
        array &$options
    ) {
        $optionValue = $row->getData($optionKey);
        if ($optionValue) {
            $optionHash = $this->createSelectRenderer($optionSource)->calcOptionHash($optionValue);
            $options['option_' . $optionHash] = 'selected="selected"';
        }
    }

    protected function _prepareArrayRow(DataObject $row)
    {
        $options = [];
        $this->markOptionAsSelected($row, 'todo', $this->todoOptions, $options);
        $row->setData('option_extra_attrs', $options);
    }

    protected function _prepareToRender()
    {
        $this->addColumn('todo', [
            'label' => __('Todo'),
            'class' => 'required-entry',
            'style' => 'width:150px',
            'renderer' => $this->createSelectRenderer($this->todoOptions)
        ]);

        $this->addColumn('comment', [
            'label' => __('Comment'),
            'class' => 'required-entry',
            'style' => 'width:75px'
        ]);

        $this->_addAfter = false;
        $this->_addButtonLabel = (string)__('Add Todo Comment');
    }

    private function createSelectRenderer(OptionSourceInterface $optionSource)
    {
        $layout = $this->getLayout();
        /** @var Select $htmlSelect */
        $htmlSelect = $layout->createBlock(
            Select::class,
            '',
            ['data' => ['is_render_to_js_template' => true]]
        );

        return $htmlSelect->setOptions(
            $optionSource->toOptionArray()
        );
    }
}

app/code/PeterRusin/DynamicField/Block/Adminhtml/System/Config/Fom/Field/Select.php

<?php
declare(strict_types=1);

namespace PeterRusin\DynamicField\Block\Adminhtml\System\Config\Form\Field;

class Select extends \Magento\Framework\View\Element\Html\Select
{
    public function setInputId(string $id)
    {
        $this->setId($id);

        return $this;
    }

    public function setInputName(string $name)
    {
        $this->setName($name);

        return $this;
    }
}

Reading

https://devdocs.magento.com/guides/v2.4/ext-best-practices/tutorials/dynamic-row-system-config.html#step-2-create-the-block-class-to-describe-custom-field-columns

Got a little advice for any of you wanting to take Adobe certification.

Never, ever, ever try to take it on Linux distribution.

Why?

Let me share with you a little story from today.

It all started when I decided to join Joseph Maxwell’s certification challenge to get my first Adobe certificate ever.

I decided to go with Professional Developer one, to sharpen my teeth before monstrous Expert Developer, and frightening Master Developer.

So… I joined the Swift Otter Study Group led by Łukasz Bajsarowicz that explained the stuff (great meeting btw), and afterward, I decided to test what I’m supposed to catch up with by taking the practice test.

I ended up having like 84% on the first attempt, and 86% on the second.

Seems like I’m ready, right?

Well… I was deeply wrong about that.

I scheduled my exam on Monday, setting it up for today, at 8 am.

I got up early, cleaned my desk, and scrolled through study materials for the last time before the approach.

It’s 7:55 am, I’m at the Examity website, ready to go.

And well… that timestamp can be treated as the beginning of a series of unfortunate events that were about to happen.

So a button popped up, instructing me to connect with my proctor to verify my identity and proceed with the certification.

“let’s get this done quickly”

So I clicked it, and it opened a new window with a request to join the zoom meeting.

Great, I don’t have zoom installed. But wait, Peter. They support browser meetings as well. Let’s just go on with that, it’s going to work for sure.

Spoiler alert: it didn’t.

This popup window changed into an actual zoom meeting and the proctor asked me to turn my camera, microphone, and share the screen.

Cool, that’s simple. So I turn my microphone and camera. So far so good. And… I’m about to turn the share screen on when this little bastard just hangs and crashes.

Okay, gotta reconnect.

So I go into the Examity to click that button again that opened the previous call, and guess what? It’s not there. Like… anywhere. It ceased to exist.

What I’m supposed to do right now? Support ticket!

I noticed this little chat box at the bottom right and messaged Examity support about my issue.

They quickly pointed me towards the zoom meeting again, and I connected.

This wasn’t too smart but I actually tried the same I did before, expecting different results.

Well… this time my camera hung, displaying as turned on from my side (light indicator on, etc) but the proctor didn’t see anything.

Okay, this isn’t going to work.

“Can I reconnect to solve this?” – I anxiously asked.

“Yes, go on and reconnect” – the proctor silently responded.

Okay. I disconnected and quickly downloaded the zoom desktop application, and learning from my previous mistakes I backed up the zoom meeting link inside the text editor.

Once I got there using the desktop application I got my microphone up, camera up, and when I tried to share the screen…

“Zoom unexpectedly closed”

This is going to be fun.

I tried to reconnect using the same link, and it turned out that my copy is still inside that meeting.

My proctor then decided to kick one of us.

Unfortunately, ended up wrong me, and ended up kicking, well… me.

So I got this message that I just got kicked outside the meeting, and I try to rejoin it.

“You can’t join meetings you’ve been kicked of” popped up three consecutive tries.

“This looks terrible, I won’t be able to join.” – I thought

But well… I quickly realized something, a gem that I had just recently discovered. Eximity support friends.

Yet again I messaged them, explaining what just happened, and they pointed me towards another Zoom meeting link, and this one worked.

I managed to connect.

I set everything up correctly this time without a crash (huge relief).

Then, I showed my ID, and later what was inside my room, and on my desk.

Yet another challenge was just ahead of me.

There is this requirement that you can’t be running any application while taking the exam, and your task manager must be checked before you can take your approach.

Guess what. I’m on Ubuntu. And apparently, my proctor did not know what it is.

How I’m supposed to show that no background tasks are running?

<click> bulb inside my head turned on, ps -aux should do the job.

And it did.

I also had to close all applications. None was running but I added multiple shortcuts like PHPStorm, Slack, Terminal, GEdit to favorites.

I quickly decided to get rid of them all, and I was finally able to approach the certification.

It went smoothly (I’m not counting here one disconnect I had due to a poor ISP provider).

In the end, I ended up passing the certification, 45/60 (75%)

I also learned a very important lesson.

Never, ever, ever try to take Adobe certifications on Linux distribution.


PR

Bash scripts that I use

#!/bin/bash
alias bm="bin/magento"

function phpvm() {
  sudo update-alternatives --config php
  sudo update-alternatives --config php-fpm.sock
  sudo service nginx restart
}

function xdebug() {
 php_version=$(readlink -f /usr/bin/php | sed 's/\/usr\/bin\///g')

 if [ "$1" != "on" ] && [ "$1" != "off" ]; then
  echo "usage: xdebug [on/off]"
  return 0
 elif [ "$1" = "on" ]; then
  sudo phpenmod xdebug && sudo service "${php_version}-fpm" restart && sudo service nginx restart
 elif [ "$1" = "off" ]; then
  sudo phpdismod xdebug && sudo service "${php_version}-fpm" restart && sudo service nginx restart
 fi
}

Copy and paste it into ~/.bashrc and reopen the terminal to make it work.

Explaination

Aliasing bin/magento helps you to execute Magento commands faster.

You also do not have to type out the full command names. You can just type the shortest unambiguous name to run a command. So if there are non-clashing commands, then you can run cache:clean like this:
bm c:c

Read more: Symfony Console Shortcut Syntax

phpvm is a handy command that lets you change the currently used PHP version.

xdebug [on/off] is pretty much self-explanatory. It enables or disables Xdebug.

File: pub/script.php

<?php
use Magento\Framework\App\Area;
use Magento\Framework\App\Bootstrap;
require __DIR__ . '/../app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();
/** @var \Magento\Framework\App\State $state */
$state = $obj->get(\Magento\Framework\App\State::class);
$state->setAreaCode(Area::AREA_FRONTEND);

// do something

Execute it via browser by navigating to localhost/script.php