From 22ba3b45eeaa5b8c9a31f5ce4919a6e8383f97bb Mon Sep 17 00:00:00 2001
From: "factory-droid[bot]"
<138933559+factory-droid[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 00:05:11 +0000
Subject: [PATCH 1/3] Add Enhance platform monitoring
- Created software/enhance.php to monitor Enhance platform releases
- Scrapes release notes from enhance.com/support/release-notes.html
- Parses HTML to extract version numbers, release dates, and latest markers
- Supports 19+ version branches (12.6, 12.5, 12.4, etc.)
- Filters out non-core versions (Appcd, WHMCS modules)
- Detects latest version (currently 12.6.0) with announcement links
- Handles patch versions correctly (keeps highest per branch)
- Uses robust HTML parsing with proper error handling
---
software/enhance.php | 191 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 191 insertions(+)
create mode 100644 software/enhance.php
diff --git a/software/enhance.php b/software/enhance.php
new file mode 100644
index 0000000..ffa11df
--- /dev/null
+++ b/software/enhance.php
@@ -0,0 +1,191 @@
+uri);
+ return $data;
+ }
+
+ function get_versions($data = '')
+ {
+ if (empty($data)) {
+ return array();
+ }
+
+ // Initialize versions array
+ $versions = array();
+ $branch_versions = array();
+
+ // Use DOMDocument for proper HTML parsing
+ $dom = new DOMDocument();
+
+ // Suppress warnings for malformed HTML
+ libxml_use_internal_errors(true);
+
+ // Load the HTML content
+ $dom->loadHTML($data);
+
+ // Reset errors
+ libxml_clear_errors();
+
+ // Get all H1 elements (version headers)
+ $h1Elements = $dom->getElementsByTagName('h1');
+
+ foreach ($h1Elements as $h1) {
+ $version_text = trim($h1->textContent);
+
+ // Skip non-core versions (Appcd, WHMCS module, etc.)
+ if (preg_match('/^(Appcd|WHMCS module|PHP packages)/i', $version_text)) {
+ continue;
+ }
+
+ // Extract version number using regex
+ if (preg_match('/^(\d+\.\d+\.\d+)/', $version_text, $matches)) {
+ $version = $matches[1];
+ } else {
+ // Skip entries that don't start with a version number
+ continue;
+ }
+
+ // Extract version components
+ $version_parts = explode('.', $version);
+ if (count($version_parts) < 3) {
+ continue;
+ }
+
+ // Determine branch (major.minor)
+ $branch = $version_parts[0] . '.' . $version_parts[1];
+
+ // Initialize version info
+ $version_info = array(
+ 'version' => $version,
+ 'release_date' => date("Y-m-d H:i:s"), // Default to current date/time
+ 'estimated' => true
+ );
+
+ // Find the release date (in H3 tag after the H1)
+ $node = $h1->nextSibling;
+ while ($node) {
+ if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'h3') {
+ $date_text = trim($node->textContent);
+
+ // Try to parse the date (format like "27th May 2025")
+ if (($timestamp = strtotime($date_text)) !== false) {
+ $version_info['release_date'] = date("Y-m-d H:i:s", $timestamp);
+ $version_info['estimated'] = false;
+ }
+ break;
+ }
+ $node = $node->nextSibling;
+ }
+
+ // Check if this is the latest version by looking for "Latest" text
+ $node = $h1;
+ $is_latest = false;
+
+ // Look for "Latest" text in the next few siblings
+ $sibling_count = 0;
+ $max_siblings_to_check = 10; // Limit how far we look
+
+ while ($node && $sibling_count < $max_siblings_to_check) {
+ $node = $node->nextSibling;
+ $sibling_count++;
+
+ if (!$node) {
+ break;
+ }
+
+ // Check text nodes and element nodes
+ if ($node->nodeType === XML_TEXT_NODE) {
+ if (stripos($node->textContent, 'Latest') !== false) {
+ $is_latest = true;
+ break;
+ }
+ } elseif ($node->nodeType === XML_ELEMENT_NODE) {
+ // Skip to next H1 (which would be the next version)
+ if ($node->tagName === 'h1') {
+ break;
+ }
+
+ // Check if this element contains "Latest"
+ if (stripos($node->textContent, 'Latest') !== false) {
+ $is_latest = true;
+ break;
+ }
+ }
+ }
+
+ // Add announcement link for latest version
+ if ($is_latest) {
+ $version_info['announcement'] = 'https://enhance.com/support/release-notes.html#' . $version;
+ }
+
+ // Store in branch_versions for later processing
+ if (!isset($branch_versions[$branch])) {
+ $branch_versions[$branch] = array();
+ }
+
+ $branch_versions[$branch][] = array(
+ 'version' => $version,
+ 'info' => $version_info,
+ 'patch' => (int)$version_parts[2]
+ );
+ }
+
+ // For each branch, keep only the highest patch version
+ foreach ($branch_versions as $branch => $branch_data) {
+ // Sort by patch version (descending)
+ usort($branch_data, function($a, $b) {
+ return $b['patch'] - $a['patch'];
+ });
+
+ // Keep only the highest patch version
+ if (!empty($branch_data)) {
+ $versions[$branch] = $branch_data[0]['info'];
+ }
+ }
+
+ return $versions;
+ }
+}
From a04272bc1a0a1f63361bf85223a57e668d080953 Mon Sep 17 00:00:00 2001
From: "factory-droid[bot]"
<138933559+factory-droid[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 00:06:01 +0000
Subject: [PATCH 2/3] Add comprehensive documentation for Enhance integration
- Created ENHANCE_INTEGRATION.md with setup and troubleshooting guide
- Includes technical details on HTML parsing implementation
- Provides testing commands and common issue resolution
- Documents data extraction format and future improvement ideas
---
ENHANCE_INTEGRATION.md | 139 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 139 insertions(+)
create mode 100644 ENHANCE_INTEGRATION.md
diff --git a/ENHANCE_INTEGRATION.md b/ENHANCE_INTEGRATION.md
new file mode 100644
index 0000000..db18f1e
--- /dev/null
+++ b/ENHANCE_INTEGRATION.md
@@ -0,0 +1,139 @@
+# Enhance Platform Integration
+_Comprehensive documentation for the `software/enhance.php` checker_
+
+---
+
+## 1 What We’ve Built
+`software/enhance.php` adds **real-time version monitoring** for the [Enhance](https://enhance.com) hosting platform to VersionCheck.
+Key capabilities:
+
+* Scrapes the public **Release Notes** page
+ `https://enhance.com/support/release-notes.html`
+* Parses every core release (v9 → v12) including patch versions
+ (e.g. `12.6.0`, `12.5.1`, `12.0.30`)
+* Detects which release is flagged **“Latest”** and records an _announcement_
+ link for notification e-mails
+* Stores/updates one entry per **major.minor branch**
+ (`12.6`, `12.5`, `11.0`, …) keeping only the highest patch
+
+The integration means administrators and subscribers will be alerted within
+minutes of any new Enhance release.
+
+---
+
+## 2 How It Works
+
+| Phase | Details |
+|-------|---------|
+| **Fetch** | `http::get()` (cURL) downloads the raw HTML. |
+| **Parse** | `DOMDocument` iterates `
` elements (each version header). |
+| **Filter** | Non-core headers (`Appcd…`, `WHMCS…`, `PHP packages`) are ignored. |
+| **Extract** | • Version number (`^\d+\.\d+\.\d+`)
• Branch = first two octets
• Release date from the first subsequent `` (ex “27th May 2025”) – converted to `Y-m-d H:i:s`.
• “Latest” determined by scanning sibling nodes for the word **Latest** (case-insensitive). |
+| **Reduce** | All patch releases within the same branch are sorted by PATCH descending; only newest kept. |
+| **Return** | Array structure consumed by `check.php`: ```php ['12.6' => ['version'=>'12.6.0','release_date'=>'2025-05-27 00:00:00','announcement'=>..., 'estimated'=>false], …]``` |
+
+---
+
+## 3 Data Extracted
+
+| Field | Example | Notes |
+|------------------|--------------------------|-----------------------------------------|
+| `software` | `enhance` (class name) | Stored automatically by core logic |
+| `branch` | `12.6` | Major.Minor |
+| `version` | `12.6.0` | Highest patch for branch |
+| `release_date` | `2025-05-27 00:00:00` | Parsed; fallback = now + `estimated=1` |
+| `announcement` | `https://enhance.com/support/release-notes.html#12.6.0` | Only present for branch marked **Latest** |
+| `estimated` | `0`/`1` | `1` if date parsing failed |
+
+---
+
+## 4 Setup Instructions
+
+1. **Pull the code**
+ ```bash
+ git fetch origin
+ git checkout feature/add-enhance-platform
+ ```
+
+2. **Verify file exists**
+ ```
+ software/enhance.php
+ ```
+
+3. **Enable (optional)**
+ `public static $enabled = true;` by default.
+ Set to `false` to disable without deleting the file.
+
+4. **Database**
+ No schema changes. `check.php` will auto-insert rows into `versions` and
+ `notifications` tables.
+
+5. **Cron**
+ Ensure your existing cron that executes `check.php` remains active
+ (per installation guide). No additional jobs are required.
+
+---
+
+## 5 Testing & Troubleshooting
+
+### Quick CLI Test
+```bash
+php -r "
+include 'software/enhance.php';
+\$e = new enhance();
+\$versions = \$e->get_versions(\$e->get_data());
+print_r(array_slice(\$versions,0,3,true));
+"
+```
+
+Expected trimmed output:
+```
+Array
+(
+ [12.6] => Array
+ (
+ [version] => 12.6.0
+ [release_date] => 2025-05-27 00:00:00
+ [announcement] => https://enhance.com/support/release-notes.html#12.6.0
+ [estimated] =>
+ )
+ [12.5] => Array
+ (
+ [version] => 12.5.1
+ [release_date] => 2025-05-21 00:00:00
+ [estimated] =>
+ )
+ [12.4] => …
+)
+```
+
+### Common Issues
+
+| Symptom | Possible Cause | Resolution |
+|---------|----------------|------------|
+| **`RuntimeException` from `http::get`** | cURL blocked / TLS failure | Check outbound HTTPS, update CA bundle |
+| **No versions detected** | HTML structure changed | Inspect page, update DOM traversal/regex |
+| **Dates show “estimated”** | Release lacks `` date or new format | Raise issue; consider regex fallback |
+| **Latest not flagged** | “Latest” label moved | Adjust sibling scan range (`$max_siblings_to_check`) |
+| **Database errors on first run** | Missing `config.php` or wrong credentials | Follow install README – import SQL & configure PDO |
+
+---
+
+## 6 Future Improvements
+
+* **Caching / Rate-limit** – store fetched HTML to cut traffic & guard against
+ temporary site outages.
+* **Unit tests** – mock HTML fixtures to detect parser breakage early.
+* **Full historical capture** – store *all* patch versions, not just newest.
+* **Security hardening** – sanitize/limit DOM parsing to reduce attack surface.
+* **Graceful fallback** – if DOM parsing fails, attempt pure regex extraction.
+* **Official API** – migrate to JSON endpoint if Enhance publishes one.
+* **Notification granularity** – allow subscribers to opt into
+ major/minor/patch channels separately.
+
+---
+
+### Maintainer Notes
+* Written for **PHP ≥8.0** (uses `JSON_THROW_ON_ERROR` in other checkers).
+* Keep `$uri` constant if domain changes (e.g. `.html` vs no extension).
+* Test after core panel redesigns – header hierarchy may shift.
From d681d6fb10cb3da3f59a5fc49ede6f8d97801d74 Mon Sep 17 00:00:00 2001
From: "factory-droid[bot]"
<138933559+factory-droid[bot]@users.noreply.github.com>
Date: Mon, 2 Jun 2025 02:46:48 +0000
Subject: [PATCH 3/3] Switch Enhance monitoring from HTML scraping to APT
repository
- Changed data source from release notes HTML to apt.enhance.com Packages file
- Now monitors ecp-core package versions directly from APT repository
- Extracts actual repository date from Release file (2025-05-27)
- Improved reliability and accuracy of version detection
- Maintains same version branch logic (12.6, 12.5, etc.)
- Only marks highest version (12.6.0) as latest with announcement link
---
software/enhance.php | 233 ++++++++++++++++++-------------------------
1 file changed, 96 insertions(+), 137 deletions(-)
diff --git a/software/enhance.php b/software/enhance.php
index ffa11df..9dfc1df 100644
--- a/software/enhance.php
+++ b/software/enhance.php
@@ -3,33 +3,27 @@
include_once(__DIR__ . '/../methods/http.php');
/*
-Example structure of Enhance release notes:
-
-# 12.6.0
-
-### 27th May 2025
-
-Latest
-
-**_Important: You must update all the servers in your cluster when applying this update._**
-
-### Enhanced
-
-- Reinstated backup failure logging to the activity log for customers on version 12.x.
-- Moved logic for management of local .my.cnf file to appcd service for improved performance.
-
-### Fixed
-
-- Race condition where the local .my.cnf had been modified and website files were restored before databases which prevented a restore of the database.
-- Descriptive text for email rate limits was incorrect in English language.
-
-# 12.5.1
-
-### 21st May 2025
-
-### Fixed
-
-- Using SSO when a previously expired session existed would require a hard refresh.
+Example structure of Enhance apt repository Packages file:
+
+Package: ecp-core
+Version: 12.6.0
+Architecture: amd64
+Maintainer: Enhance Ltd
+Installed-Size: 87858
+Depends: cron, curl, ecp-php56, ecp-php70, ecp-php71, ecp-php72, ecp-php73, ecp-php74, ecp-php80, ecp-php81, ecp-php82, ecp-php83, ecp-php84, libc6 (>= 2.34), libc6 (>= 2.38), libc6 (>= 2.39), libmilter1.0.1 (>= 8.14.1), libnss3-dev, libpam0g (>= 0.99.7.1), libssl3t64 (>= 3.0.0), linux-image-extra-virtual, openssh-server, openssl, quota, rsync, ufw, zlib1g (>= 1:1.1.4)
+Conflicts: appcd
+Replaces: appcd
+Filename: pool/noble/ecp-core_12.6.0_amd64.deb
+Size: 22096584
+MD5sum: b8d084689177aa18b52ff7ee01443156
+SHA1: b41084091113c5090f49a57f4f77b0f2d5774eec
+SHA256: 4be9b14e6004debcdbc04819d07f2e78e2624760178cd7e72041722902f87704
+Priority: optional
+Description: [generated from Rust crate appcd]
+
+Package: ecp-core
+Version: 12.5.1
+...
*/
class enhance extends SoftwareCheck
@@ -37,140 +31,100 @@ class enhance extends SoftwareCheck
public static $name = 'Enhance Platform';
public static $vendor = 'Enhance';
public static $homepage = 'https://enhance.com/';
- public static $type = 'html';
+ public static $type = 'apt';
public static $enabled = true;
- var $uri = 'https://enhance.com/support/release-notes.html';
+ var $uri = 'https://apt.enhance.com/dists/noble/main/binary-amd64/Packages';
+ var $release_uri = 'https://apt.enhance.com/dists/noble/Release';
function get_data()
{
- $data = http::get($this->uri);
- return $data;
+ // Get the Packages file
+ $packages_data = http::get($this->uri);
+
+ // Get the Release file for date information
+ try {
+ $release_data = http::get($this->release_uri);
+ } catch (Exception $e) {
+ // If we can't get the Release file, just use the Packages data
+ $release_data = '';
+ }
+
+ return [
+ 'packages' => $packages_data,
+ 'release' => $release_data
+ ];
}
- function get_versions($data = '')
+ function get_versions($data = array())
{
- if (empty($data)) {
+ if (empty($data) || !isset($data['packages']) || empty($data['packages'])) {
return array();
}
- // Initialize versions array
- $versions = array();
- $branch_versions = array();
-
- // Use DOMDocument for proper HTML parsing
- $dom = new DOMDocument();
+ // Extract repository date from Release file if available
+ $repo_date = date("Y-m-d H:i:s"); // Default to current date/time
+ $estimated = true;
- // Suppress warnings for malformed HTML
- libxml_use_internal_errors(true);
-
- // Load the HTML content
- $dom->loadHTML($data);
+ if (!empty($data['release'])) {
+ if (preg_match('/Date:\s+([^\n]+)/', $data['release'], $matches)) {
+ $date_str = trim($matches[1]);
+ if (($timestamp = strtotime($date_str)) !== false) {
+ $repo_date = date("Y-m-d H:i:s", $timestamp);
+ $estimated = false;
+ }
+ }
+ }
- // Reset errors
- libxml_clear_errors();
+ // Parse Packages file
+ $versions = array();
+ $branch_versions = array();
+ $latest_version = null;
+ $latest_version_full = null;
- // Get all H1 elements (version headers)
- $h1Elements = $dom->getElementsByTagName('h1');
+ // Split the Packages file into individual package entries
+ $package_entries = explode("\n\n", $data['packages']);
- foreach ($h1Elements as $h1) {
- $version_text = trim($h1->textContent);
-
- // Skip non-core versions (Appcd, WHMCS module, etc.)
- if (preg_match('/^(Appcd|WHMCS module|PHP packages)/i', $version_text)) {
+ foreach ($package_entries as $entry) {
+ // Check if this is an ecp-core package
+ if (strpos($entry, 'Package: ecp-core') === false) {
continue;
}
- // Extract version number using regex
- if (preg_match('/^(\d+\.\d+\.\d+)/', $version_text, $matches)) {
+ // Extract version
+ if (preg_match('/Version:\s+(\d+\.\d+\.\d+)/', $entry, $matches)) {
$version = $matches[1];
- } else {
- // Skip entries that don't start with a version number
- continue;
- }
-
- // Extract version components
- $version_parts = explode('.', $version);
- if (count($version_parts) < 3) {
- continue;
- }
-
- // Determine branch (major.minor)
- $branch = $version_parts[0] . '.' . $version_parts[1];
-
- // Initialize version info
- $version_info = array(
- 'version' => $version,
- 'release_date' => date("Y-m-d H:i:s"), // Default to current date/time
- 'estimated' => true
- );
-
- // Find the release date (in H3 tag after the H1)
- $node = $h1->nextSibling;
- while ($node) {
- if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'h3') {
- $date_text = trim($node->textContent);
-
- // Try to parse the date (format like "27th May 2025")
- if (($timestamp = strtotime($date_text)) !== false) {
- $version_info['release_date'] = date("Y-m-d H:i:s", $timestamp);
- $version_info['estimated'] = false;
- }
- break;
+
+ // Extract version components
+ $version_parts = explode('.', $version);
+ if (count($version_parts) < 3) {
+ continue;
}
- $node = $node->nextSibling;
- }
-
- // Check if this is the latest version by looking for "Latest" text
- $node = $h1;
- $is_latest = false;
-
- // Look for "Latest" text in the next few siblings
- $sibling_count = 0;
- $max_siblings_to_check = 10; // Limit how far we look
-
- while ($node && $sibling_count < $max_siblings_to_check) {
- $node = $node->nextSibling;
- $sibling_count++;
- if (!$node) {
- break;
+ // Determine branch (major.minor)
+ $branch = $version_parts[0] . '.' . $version_parts[1];
+ $patch = (int)$version_parts[2];
+
+ // Store in branch_versions for later processing
+ if (!isset($branch_versions[$branch])) {
+ $branch_versions[$branch] = array();
}
- // Check text nodes and element nodes
- if ($node->nodeType === XML_TEXT_NODE) {
- if (stripos($node->textContent, 'Latest') !== false) {
- $is_latest = true;
- break;
- }
- } elseif ($node->nodeType === XML_ELEMENT_NODE) {
- // Skip to next H1 (which would be the next version)
- if ($node->tagName === 'h1') {
- break;
- }
-
- // Check if this element contains "Latest"
- if (stripos($node->textContent, 'Latest') !== false) {
- $is_latest = true;
- break;
- }
+ $branch_versions[$branch][] = array(
+ 'version' => $version,
+ 'patch' => $patch,
+ 'info' => array(
+ 'version' => $version,
+ 'release_date' => $repo_date,
+ 'estimated' => $estimated
+ )
+ );
+
+ // Track the latest version (highest version number)
+ if ($latest_version === null || version_compare($version, $latest_version_full, '>')) {
+ $latest_version = $branch;
+ $latest_version_full = $version;
}
}
-
- // Add announcement link for latest version
- if ($is_latest) {
- $version_info['announcement'] = 'https://enhance.com/support/release-notes.html#' . $version;
- }
-
- // Store in branch_versions for later processing
- if (!isset($branch_versions[$branch])) {
- $branch_versions[$branch] = array();
- }
-
- $branch_versions[$branch][] = array(
- 'version' => $version,
- 'info' => $version_info,
- 'patch' => (int)$version_parts[2]
- );
}
// For each branch, keep only the highest patch version
@@ -186,6 +140,11 @@ function get_versions($data = '')
}
}
+ // Mark only the highest version as the latest with an announcement link
+ if ($latest_version !== null && isset($versions[$latest_version])) {
+ $versions[$latest_version]['announcement'] = 'https://enhance.com/support/release-notes.html#' . $latest_version_full;
+ }
+
return $versions;
}
}