Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 4 additions & 23 deletions Api/PageTypeDetectorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,16 @@
namespace BasicRum\Analytics\Api;

/**
* Interface for page type detection service
* Resolves a coarse-grained page type for the current storefront request.
*
* @api
*/
interface PageTypeDetectorInterface
{
/**
* Get the current page type
* Get the current page type (e.g. "home", "product", "checkout", "404_not_found").
*
* @return string
*/
public function getPageType(): string;

/**
* Check if current page is homepage
*
* @return bool
*/
public function isHomePage(): bool;

/**
* Check if current page is a product page
*
* @return bool
*/
public function isProductPage(): bool;

/**
* Check if current page is a checkout page
*
* @return bool
*/
public function isCheckoutPage(): bool;
}
10 changes: 6 additions & 4 deletions Block/Adminhtml/System/Config/BoomerangVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@

namespace BasicRum\Analytics\Block\Adminhtml\System\Config;

use BasicRum\Analytics\Model\Boomerang;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;

class BoomerangVersion extends Field
{
protected function _getElementHtml(AbstractElement $element)
protected function _getElementHtml(AbstractElement $element): string
{
return 'Boomerang JS v. 1.815.60 - cutting-edge - 30 KB (gzipped)';
return $this->escapeHtml(
sprintf('Boomerang JS v.%s (continuity flavor) - %s', Boomerang::VERSION, Boomerang::SIZE_HINT)
);
}
}
}
17 changes: 0 additions & 17 deletions Block/Adminhtml/System/Config/ConsentMode.php

This file was deleted.

32 changes: 18 additions & 14 deletions Block/Adminhtml/System/Config/Logo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@
namespace BasicRum\Analytics\Block\Adminhtml\System\Config;

use Magento\Config\Block\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Data\Form\Element\AbstractElement;

class Logo extends Field
{
public function __construct(
Context $context,
array $data = []
) {
parent::__construct($context, $data);
/**
* @var string
*/
protected $_template = 'BasicRum_Analytics::system/config/logo.phtml';

/**
* Render the field as a full-width banner (no label/scope columns).
*/
public function render(AbstractElement $element): string
{
return $this->_toHtml();
}

public function render(AbstractElement $element)
protected function _getElementHtml(AbstractElement $element): string
{
$html = '<div style="margin: 20px 0; text-align: center; font-size: 3rem;">';
$html .= '<img src="' . $this->getViewFileUrl('BasicRum_Analytics::images/basicrum-log.svg') . '" alt="BasicRum Logo" style="width: 35px; height: 35px;" />';
$html .= 'BasicRUM Analytics';
$html .= '</div>';
return $html;
return $this->_toHtml();
}

protected function _getElementHtml(AbstractElement $element)
/**
* Static URL of the BasicRUM logo asset.
*/
public function getLogoUrl(): string
{
return $this->render($element);
return $this->getViewFileUrl('BasicRum_Analytics::images/basicrum-log.svg');
}
}
97 changes: 97 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Changelog

All notable changes to the BasicRum Analytics module are documented here.
This project adheres to [Semantic Versioning](https://semver.org/).

## [0.1.0]

A compatibility, correctness, security and cleanup release. Two of the changes fix real functional
bugs (beacons were not actually initialised; beacons were blocked under strict CSP). The public
configuration path (`basicrum/general/*`) is unchanged, so existing configuration keeps working.

### Fixed (functional bugs)

- **Beacons are now actually initialised.** The frontend template built a `basicRumBoomerangConfig`
object (beacon URL, cookie flags, ResourceTiming/Continuity settings) but **never passed it to
Boomerang**, so those settings were inert. The bootstrap now initialises Boomerang through its method
queue: `BOOMR_mq.push(["init", config])`. _Why:_ without an `init` call Boomerang never learns the
beacon URL, so beacons could not be delivered as configured.
- **Deferred send reimplemented with the public API.** The old code defined a custom
`BOOMR.plugins.WaitAfterOnload` object that was never registered in Boomerang's plugin list, so it did
nothing. The "send a fixed delay after the page loads" behaviour is now implemented with the supported
`autorun: false` config plus a delayed `BOOMR.page_ready()` call on window `load`. _Why:_ custom
plugins must be compiled into the Boomerang build to run; the public `page_ready()` call achieves the
same intent reliably.
- **Content Security Policy: beacon host is whitelisted automatically.** Added
`Model/Csp/BeaconPolicyCollector` (a `Magento\Csp\Api\PolicyCollectorInterface`) that reads the
configured Beacon Endpoint and adds its origin to the `connect-src` and `img-src` fetch policies.
_Why:_ Magento 2.4+ ships strict CSP. Boomerang delivers beacons via XHR / `navigator.sendBeacon`
(`connect-src`) and image beacons (`img-src`); on a CSP-enforcing store those requests were blocked.
Because the endpoint is admin-configurable it cannot be a static `csp_whitelist.xml` entry, so the
host is resolved at runtime.

### Added

- **Beacon Token / Site Key field** (`basicrum/general/token`). Optional, stored **encrypted**
(`obscure` field + `Encrypted` backend model). When set, the ViewModel decrypts it and appends it to the
beacon URL automatically as `&token=…` (URL-encoded, respecting any existing query string). Leave blank
for collectors that need no token. The CSP whitelist is unaffected (the token lives in the query string,
not the host).

### Changed (compatibility)

- **PHP 8.3 / 8.4 support.** `composer.json` PHP constraint changed from `^8.1|^8.2|^8.3`
(which *excluded* 8.4) to `~8.3.0 || ~8.4.0`. _Why:_ Magento 2.4.8 requires PHP 8.3 and 2.4.9 adds
8.4; the old constraint blocked installation on supported platforms.
- **Real dependency constraints.** Replaced the `magento/framework: *` / `magento/module-store: *`
wildcards with bounded ranges, and added the modules actually used at runtime:
`magento/module-config`, `magento/module-backend`, `magento/module-csp`.
- **Module load order.** `etc/module.xml` now declares a `<sequence>` for `Magento_Store`,
`Magento_Config`, `Magento_Backend` and `Magento_Csp`. _Why:_ the module relies on these at runtime;
declaring them guarantees correct load/upgrade order.
- **`PageTypeDetector` uses the correct response type.** It injected the generic
`App\ResponseInterface` and then called `getStatusCode()`, which that interface does not declare. It
now injects `App\Response\Http`, uses a strict `=== 404` comparison, and maps `cms_noroute_index` to
the 404 page type. The result is memoised.

### Changed (code quality & security)

- `ViewModel/Footer` exposes a typed, null-safe `getBeaconEndpoint(): string` (returns `''` when unset)
and the config path constant `XML_PATH_BEACON_ENDPOINT`, replacing the loosely-typed `getConfig()`
array that could leak `null` into the template.
- The frontend template renders nothing when no endpoint is configured, standardises on the injected
`$escaper->escapeJs()`, drops a redundant view-model re-fetch, and keeps the CSP-safe
`SecureHtmlRenderer` script rendering.
- `Api\PageTypeDetectorInterface` trimmed to the single method in use (`getPageType()`); the unused
`isHomePage()/isProductPage()/isCheckoutPage()` methods were removed.
- Admin config field blocks gained return types. The `Logo` field's markup moved out of PHP into
`view/adminhtml/templates/system/config/logo.phtml` + `view/adminhtml/web/css/basicrum-config.css`
(loaded via `adminhtml_system_config_edit.xml`), removing inline styles and a redundant constructor.
- The Boomerang version string is centralised in `Model\Boomerang` (single source of truth) instead of
being duplicated across the JS filename and the admin display block.
- Beacon Endpoint field now validates as a URL (`validate-url`) and documents the CSP behaviour in its
admin comment.
- Added a real `LICENSE` (MIT for the module) with a **BSD-3-Clause third-party notice** for the bundled
Boomerang library, and corrected the README (it previously claimed PHP 7.2 / Magento 2.3).

### Removed

- **Non-functional consent UI.** The `consent` configuration group and its `ConsentMode` source model
were removed. _Why:_ the fields were exposed in the admin but never read by any code, so they gave a
false impression that consent gating was in effect. (Consent gating can be reintroduced as a real
feature later.)

### Notes

- The vendored Boomerang file was renamed from `boomerang-1.815.60.cutting-edge.min.js` to the
version-agnostic `boomerang.min.js`. Investigation confirmed this is **release build 815 with the
"cutting-edge" plugin flavor** (which bundles the Continuity plugin) — "cutting-edge" is a
*plugin-set name*, not an unstable channel; npm `latest` (1.815.1) is the same build. The code was
kept as-is; only the filename and version bookkeeping changed.
- To upgrade the bundled library later, replace `view/frontend/web/js/boomr/boomerang.min.js` and update
`Model\Boomerang::VERSION`. No template change is required (the path is version-agnostic).

## [0.0.2] — previous

- Initial fork baseline: ViewModel-based rendering, PHP 8 constructor property promotion, removed
setup version, Boomerang build without debug logging.
57 changes: 57 additions & 0 deletions IMPROVEMENT_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# BasicRum Analytics — Improvement Plan

Cleanup + PHP 8.4 / Magento 2.4.8+ compatibility + beacon fix. Status of each item is tracked below.

## Decisions
- Target **PHP 8.3 / 8.4**, **Magento 2.4.8+**.
- Fix the beacon integration (boomerang was never `init`-ed) and add dynamic CSP for the beacon host.
- Remove the dead, non-functional consent UI.
- Ship a stable Boomerang build (not the "cutting-edge" one) with proper attribution.

## Checklist

### 1. Packaging & metadata — DONE
- [x] `composer.json`: PHP `~8.3.0 || ~8.4.0`; pinned real version ranges; added
`magento/module-config`, `magento/module-backend`, `magento/module-csp`; dropped `*` wildcards,
stale `archive.exclude`, and the module-level `repositories` block.
- [x] `LICENSE` added (MIT for module code + BSD-3-Clause third-party notice for Boomerang).
- [x] `README.md` corrected (PHP 8.3+/Magento 2.4.8+, working LICENSE link, CSP note).
- [x] `etc/module.xml`: `<sequence>` for `Magento_Store`, `Magento_Config`, `Magento_Backend`, `Magento_Csp`.

### 2. PHP cleanup & correctness — DONE
- [x] `Model/PageTypeDetector.php`: inject `Response\Http`; map `cms_noroute_index`; `=== 404`; memoize.
- [x] `Api/PageTypeDetectorInterface.php`: trimmed to `getPageType()`.
- [x] `ViewModel/Footer.php`: typed/null-safe `getBeaconEndpoint()`; scaffold comment removed; `readonly` promotion.
- [x] Admin blocks: return types added; `Logo` HTML moved to `view/adminhtml/templates/system/config/logo.phtml`
+ `css/basicrum-config.css` (via `adminhtml_system_config_edit.xml`); redundant ctor & unused imports removed.
- [x] PSR-12 fixed; `etc/di.xml` scaffold comment removed. (Note: `final` deliberately NOT added to
DI-bound classes so Magento can still generate interceptors/plugins for them.)

### 3. Beacon integration — `view/frontend/templates/footer.phtml` — DONE
- [x] Boomerang is now initialized via `BOOMR_mq.push(["init", config])` (previously never init-ed → inert).
- [x] Deferred send implemented with `autorun: false` + a delayed `BOOMR.page_ready()` on window load
(public API; replaces the never-registered custom `WaitAfterOnload` plugin).
- [x] Renders nothing when the endpoint is blank; standardized on `$escaper->escapeJs`; redundant
view-model re-fetch removed. Kept CSP-safe `SecureHtmlRenderer`.

### 4. Dynamic CSP — `Model/Csp/BeaconPolicyCollector.php` + `etc/di.xml` — DONE
- [x] Collector adds the beacon origin (scheme+host+port) to `connect-src` + `img-src`; registered in
`Magento\Csp\Model\CompositePolicyCollector`. Verified at runtime.

### 5. Remove dead consent config — DONE
- [x] `consent` group removed from `etc/adminhtml/system.xml`; `ConsentMode.php` deleted.

### 6. Boomerang asset — DONE (see note)
- [x] Renamed to version-agnostic `js/boomr/boomerang.min.js`; version centralized in `Model\Boomerang`.
- Note: investigation showed the file is **release build 815 with the "cutting-edge" *plugin flavor***
(bundles Continuity), NOT an unstable nightly — "cutting-edge" is a plugin-set name. npm `latest`
(1.815.1) is the same build. Per that finding we kept build 815 (same code, needed plugins) rather than
rebuild. To change the bundle later, replace the file and update `Model\Boomerang::VERSION`.

## Verification — DONE
- [x] `composer validate` clean; `php -l` clean on PHP 8.4; all XML validated via `setup:upgrade`.
- [x] `module:enable` + `setup:upgrade` + `cache:flush` succeeded.
- [x] Runtime (Magento-bootstrapped): interface preference resolves; ViewModel null-safe; CSP collector
emits correct policies and is registered in the composite collector.
- [ ] TODO (needs a browser + configured endpoint): confirm the beacon actually fires with `p_type` +
`p_gen=mage2` and that there are no CSP violations for the beacon host.
60 changes: 60 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
MIT License

Copyright (c) BasicRUM (Tsvetan Stoychev)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

-------------------------------------------------------------------------------

THIRD-PARTY NOTICES

This package bundles Boomerang, a Real User Monitoring library, under
view/frontend/web/js/boomr/. Boomerang is distributed under the BSD 3-Clause
License and is NOT covered by the MIT license above.

Copyright (c) 2011, Yahoo! Inc. All rights reserved.
Copyright (c) 2011-2012, Log-Normal, Inc. All rights reserved.
Copyright (c) 2012-2017, SOASTA, Inc. All rights reserved.
Copyright (c) 2017-2023, Akamai Technologies, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Boomerang project: https://github.com/akamai/boomerang
30 changes: 30 additions & 0 deletions Model/Boomerang.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);

namespace BasicRum\Analytics\Model;

/**
* Single source of truth for the bundled Boomerang RUM library.
*
* When upgrading the vendored build, update {@see self::VERSION} and replace the file the
* {@see self::JS_VIEW_PATH} view path resolves to — the path itself stays version-agnostic so the
* template never needs editing.
*/
class Boomerang
{
/**
* Bundled Boomerang build. Format: <major>.<buildCount>.<flavorRevision>.
* "60" is the "cutting-edge" plugin flavor (bundles the Continuity plugin) of release build 815.
*/
public const VERSION = '1.815.60';

/**
* Approximate transferred size, shown in the admin for information only.
*/
public const SIZE_HINT = '~30 KB (gzipped)';

/**
* Version-agnostic view path of the bundled, minified library.
*/
public const JS_VIEW_PATH = 'BasicRum_Analytics::js/boomr/boomerang.min.js';
}
Loading