fix(2.10.2): AfterInstall must be a no-namespace class
EspoCRM's extension installer (ExtensionManager.php → Base.php:397) does require_once on scripts/AfterInstall.php and then `new AfterInstall()` without any namespace prefix. The 2.10.0/2.10.1 version had `namespace Espo\Modules\SmartAssistant\Scripts;` which resolved at install time to "Class AfterInstall not found" and aborted the install with HTTP 500 from KlearBrandingExtension/action/install. Match the pattern used by GoogleIntegration/scripts/AfterInstall.php: plain top-level class, no namespace, optional `use` for the Container type hint. Same behavior (idempotent seed of 7 AssistantPrompt rows with skipAll), just loadable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -3,7 +3,7 @@
|
||||
"module": "SmartAssistant",
|
||||
"description": "Unified AI Assistant for Legal CRM — floating chat with case memory, office alerts, and AI Gateway integration",
|
||||
"author": "klear",
|
||||
"version": "2.10.1",
|
||||
"version": "2.10.2",
|
||||
"acceptableVersions": [
|
||||
">=8.0.0"
|
||||
],
|
||||
|
||||
+18
-25
@@ -1,16 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Modules\SmartAssistant\Scripts;
|
||||
/**
|
||||
* AfterInstall script for SmartAssistant extension 2.10.x.
|
||||
*
|
||||
* Idempotently seeds the AssistantPrompt entity with 7 default prompt
|
||||
* sections (tool_rules, writing_style, legal_assistance, personality_case,
|
||||
* personality_office, other_rules_case, other_rules_office). If a row with
|
||||
* the same `key` already exists it is left alone — admin edits win.
|
||||
*
|
||||
* Class must be named `AfterInstall` with NO namespace — that's what the
|
||||
* EspoCRM extension installer looks for in scripts/AfterInstall.php.
|
||||
* See application/Espo/Core/Upgrades/ExtensionManager.php scriptNames map.
|
||||
*/
|
||||
|
||||
use Espo\Core\Container;
|
||||
|
||||
/**
|
||||
* Runs after the extension is installed/upgraded.
|
||||
*
|
||||
* Idempotent: only inserts AssistantPrompt rows whose `key` doesn't exist yet.
|
||||
* If the row exists (even with edits), we leave it alone — the user's
|
||||
* customizations win.
|
||||
*/
|
||||
class AfterInstall
|
||||
{
|
||||
public function run(Container $container, $params = null): void
|
||||
@@ -18,21 +21,11 @@ class AfterInstall
|
||||
$entityManager = $container->get('entityManager');
|
||||
$log = $container->get('log');
|
||||
|
||||
$resourceFile = __DIR__ . '/../files/custom/Espo/Modules/SmartAssistant/Resources/data/default-prompts.json';
|
||||
// The JSON file lives inside the installed module resources at runtime.
|
||||
$resourceFile = 'custom/Espo/Modules/SmartAssistant/Resources/data/default-prompts.json';
|
||||
|
||||
if (!is_file($resourceFile)) {
|
||||
// Fall back to the module Resources path when the extension is
|
||||
// installed (Resources/ is the canonical location at runtime).
|
||||
$resourceFile = dirname(__DIR__) . '/files/custom/Espo/Modules/SmartAssistant/Resources/data/default-prompts.json';
|
||||
}
|
||||
|
||||
if (!is_file($resourceFile)) {
|
||||
// Final fallback: the module's installed Resources path.
|
||||
$resourceFile = 'custom/Espo/Modules/SmartAssistant/Resources/data/default-prompts.json';
|
||||
}
|
||||
|
||||
if (!is_file($resourceFile)) {
|
||||
$log->warning("[SmartAssistant] AfterInstall: default-prompts.json not found, skipping seed");
|
||||
$log->warning("[SmartAssistant] AfterInstall: $resourceFile not found, skipping seed");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +33,7 @@ class AfterInstall
|
||||
$defaults = json_decode($json, true);
|
||||
|
||||
if (!is_array($defaults)) {
|
||||
$log->warning("[SmartAssistant] AfterInstall: default-prompts.json invalid JSON");
|
||||
$log->warning("[SmartAssistant] AfterInstall: invalid JSON in $resourceFile");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,8 +66,8 @@ class AfterInstall
|
||||
'isActive' => true,
|
||||
]);
|
||||
// skipAll bypasses beforeSave hooks that require a logged-in user
|
||||
// service (install runs without a session) and ACL checks (we're
|
||||
// seeding firm-wide defaults; no user is implicated).
|
||||
// service (installer runs without an HTTP session) and ACL checks
|
||||
// (we're seeding firm-wide defaults; no user is implicated).
|
||||
$entityManager->saveEntity($entity, ['skipAll' => true]);
|
||||
$created++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user