snapshot of https://github.com/espocrm/espocrm.git (master, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Test / Test on PHP 8.3 (push) Failing after 5s
Test / Test on PHP 8.4 (push) Failing after 4s
Test / Test on PHP 8.5 (push) Failing after 3s
TypeScript static analysis / TypeScript check (push) Failing after 14s
Test Integration on MySQL / Test on PHP 8.3 (push) Failing after 1s
Test Integration on MySQL / Test on PHP 8.5 (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 fix (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 master (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-18 04:19:15 +05:00
commit 7cc7e6b4f3
10077 changed files with 900756 additions and 0 deletions

3
upgrades/10.0/data.json Normal file
View File

@@ -0,0 +1,3 @@
{
"manifest": {}
}

View File

@@ -0,0 +1,285 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Record\Service;
use Espo\Entities\Extension;
use Espo\ORM\EntityManager;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Exception
*/
public function run(Container $container): void
{
$this->container = $container;
$this->processCheckExtensions();
$this->processCheckCustomizations();
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '3.13.0', $errorMessageList);
$this->processCheckExtension('Real Estate', '1.8.5', $errorMessageList);
$this->processCheckExtension('VoIP Integration', '2.8.0', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
/**
* @noinspection PhpSameParameterValueInspection
*/
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->getByClass(EntityManager::class);
$extension = $em->getRDBRepositoryByClass(Extension::class)
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->getVersion();
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 10.0 is not compatible with '$name' extension of versions lower than $minVersion. " .
"You need to upgrade the extension.";
$errorMessageList[] = $message;
}
private function processCheckCustomizations(): void
{
/** @var array{0: string, 1: string[]}[] $data */
$data = [
[
Service::class,
[
'create',
'read',
'update',
'delete',
'link',
'unlink',
'massLink',
'beforeCreateEntity',
'afterCreateEntity',
'beforeUpdateEntity',
'afterUpdateEntity',
'beforeDeleteEntity',
'afterDeleteEntity',
]
],
];
$output = [];
foreach (self::getClasses("custom/Espo/") as $class) {
foreach ($data as $it) {
$base = $it[0];
$methods = $it[1];
try {
$isSubClass = class_exists($class) && is_subclass_of($class, $base);
} catch (Throwable) {
continue;
}
if (
$isSubClass &&
(
str_starts_with($class, "Espo\\Modules\\") ||
str_starts_with($class, "Espo\\Custom\\")
) &&
!str_starts_with($class, "Espo\\Modules\\Crm\\")
) {
foreach ($methods as $method) {
if (self::isMethodOverridden($class, $base, $method)) {
$output[] = [$class, $method];
}
}
}
}
}
if ($output === []) {
return;
}
$message = "Cannot upgrade because of incompatible method overrides. ".
"Fix the customizations and/or uninstall the incompatible extensions. " .
"You can fix it by removing the methods for the classes.\n\n";
$message .= "Problem classes and methods:\n";
foreach ($output as $it) {
$message .= ' ' . $it[0] . ': ' . $it[1] . "\n";
}
throw new Error($message);
}
/**
* @noinspection PhpSameParameterValueInspection
*/
private static function isMethodOverridden(string $child, string $base, string $method): bool
{
if (!class_exists($child) || !method_exists($child, $method)) {
return false;
}
$methodRef = new ReflectionMethod($child, $method);
return
$methodRef->getDeclaringClass()->getName() !== $base &&
$methodRef->getDeclaringClass()->getName() === $child;
}
/**
* @return string[]
* @noinspection PhpSameParameterValueInspection
*/
private static function getClasses(string $directory): array
{
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
$classes = [];
foreach ($iterator as $file) {
if (!$file->isFile()) {
continue;
}
if (self::toIgnoreFile($file)) {
continue;
}
if ($file->getExtension() !== 'php') {
continue;
}
$content = file_get_contents($file->getPathname());
$namespace = null;
$class = null;
$tokens = token_get_all($content);
for ($i = 0; $i < count($tokens); $i++) {
$token = $tokens[$i];
if (!is_array($token)) {
continue;
}
if ($token[0] === T_NAMESPACE) {
$namespace = '';
for ($j = $i + 1; $j < count($tokens); $j++) {
if (
is_array($tokens[$j]) &&
in_array($tokens[$j][0], [T_STRING, T_NAME_QUALIFIED, T_NS_SEPARATOR])
) {
$namespace .= $tokens[$j][1];
} elseif ($tokens[$j] === ';') {
break;
}
}
}
if ($token[0] === T_CLASS) {
$prev = $tokens[$i - 1] ?? null;
if (is_array($prev) && $prev[0] === T_NEW) {
continue;
}
for ($j = $i + 1; $j < count($tokens); $j++) {
if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
$class = $tokens[$j][1];
break;
}
}
if ($class) {
$classes[] = $namespace
? $namespace . '\\' . $class
: $class;
}
}
}
}
return $classes;
}
private static function toIgnoreFile(string $file): bool
{
$ignorePathRegexList = [
'^custom\/Espo\/Modules\/[^\/]+\/vendor',
'^custom\/Espo\/Custom\/vendor',
];
foreach ($ignorePathRegexList as $pattern) {
if (preg_match('/' . $pattern . '/', $file)) {
return true;
}
}
return false;
}
}

7
upgrades/5.7/data.json Normal file
View File

@@ -0,0 +1,7 @@
{
"beforeUpgradeFiles": [
"application/Espo/Core/Utils/Database/Helper.php"
],
"manifest": {
}
}

View File

@@ -0,0 +1,48 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$entityManager = $container->get('entityManager');
$entityManager->createEntity('ScheduledJob', [
'job' => 'ProcessWebhookQueue',
'name' => 'Process Webhook Queue',
'scheduling' => '*/5 * * * *',
'status' => 'Active',
]);
$config = $container->get('config');
$config->set('hashSecretKey', \Espo\Core\Utils\Util::generateApiKey());
$config->save();
}
}

View File

@@ -0,0 +1,68 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
public function run($container)
{
$this->container = $container;
$this->checkDatabaseRequirements();
$pdo = $container->get('entityManager')->getPDO();
try {
$pdo->query("TRUNCATE TABLE `scheduled_job_log_record`");
} catch (\Exception $e) {}
}
protected function checkDatabaseRequirements()
{
$databaseRequirements = [
'mysql' => '5.6.0',
'mariadb' => '10.0.0',
];
$databaseHelper = new \Espo\Core\Utils\Database\Helper($this->container->get('config'));
$databaseType = $databaseHelper->getDatabaseType();
$fullVersion = $databaseHelper->getPdoDatabaseVersion($this->container->get('entityManager')->getPDO());
if (preg_match('/[0-9]+\.[0-9]+\.[0-9]+/', $fullVersion, $match)) {
$version = $match[0];
$databaseTypeLc = strtolower($databaseType);
if (isset($databaseRequirements[$databaseTypeLc])) {
if (version_compare($version, $databaseRequirements[$databaseTypeLc], '<')) {
$msg = "Your {$databaseType} version is not supported. Please upgrade {$databaseType} to a newer version (5.6 or later).";
throw new \Espo\Core\Exceptions\Error($msg);
}
}
}
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,41 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$config = $container->get('config');
$personNameFormat = $config->get('personNameFormat');
if (!$personNameFormat) {
$config->set('personNameFormat', 'firstLast');
}
$config->save();
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,38 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$config = $container->get('config');
$config->set('streamEmailWithContentEntityTypeList', ['Case']);
$config->save();
}
}

View File

@@ -0,0 +1,52 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
public function run($container)
{
$pdo = $container->get('entityManager')->getPDO();
$query = "
UPDATE email_account SET port = NULL WHERE port = ''
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
$query = "
UPDATE inbound_email SET port = NULL WHERE port = ''
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,40 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$from = 'custom/Espo/Custom/Resources/templates/noteEmailRecieved';
$to = 'custom/Espo/Custom/Resources/templates/noteEmailReceived';
if (is_dir($from)) {
rename($from, $to);
}
}
}

7
upgrades/5.8/data.json Normal file
View File

@@ -0,0 +1,7 @@
{
"manifest": {
"delete": [
"vendor/php-mime-mail-parser"
]
}
}

View File

@@ -0,0 +1,137 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$this->container = $container;
$this->populateOpportunityContactId();
$this->manageIndexes();
$config = $container->get('config');
$config->set('personNameFormat', 'firstLast');
$config->set('streamEmailWithContentEntityTypeList', ['Case']);
$config->save();
$from = 'custom/Espo/Custom/Resources/templates/noteEmailRecieved';
$to = 'custom/Espo/Custom/Resources/templates/noteEmailReceived';
if (is_dir($from)) {
rename($from, $to);
}
}
protected function populateOpportunityContactId()
{
$pdo = $this->container->get('entityManager')->getPdo();
$sql = "
SELECT opportunity.id AS 'opportunityId', contact.id AS `contactId` FROM `opportunity`
JOIN contact_opportunity ON contact_opportunity.opportunity_id = opportunity.id AND contact_opportunity.deleted = 0
JOIN contact ON contact.id = contact_opportunity.contact_id AND contact.deleted = 0
WHERE
contact.id IN (
SELECT MIN(contact.id)
FROM `opportunity`
JOIN contact_opportunity ON contact_opportunity.opportunity_id = opportunity.id AND contact_opportunity.deleted = 0
JOIN contact ON contact.id = contact_opportunity.contact_id AND contact.deleted = 0
GROUP BY opportunity.id
) AND
opportunity.contact_id IS NULL AND
opportunity.deleted = 0
";
$sth = $pdo->prepare($sql);
$sth->execute();
while ($row = $sth->fetch()) {
$cId = $row['contactId'] ?? null;
$oId = $row['opportunityId'] ?? null;
if (!$cId || !$oId) continue;
$q = "
UPDATE `opportunity` SET contact_id = ".$pdo->quote($cId)." WHERE id = ".$pdo->quote($oId)."
";
$pdo->query($q);
}
}
protected function manageIndexes()
{
$pdo = $this->container->get('entityManager')->getPdo();
$sth = $pdo->prepare("SHOW INDEX FROM `note`");
$sth->execute();
$rows = [];
while ($row = $sth->fetch()) {
$rows[] = $row;
}
$indexes = [];
foreach ($rows as $item) {
$k = 0;
foreach ($rows as $item2) {
if ($item['Key_name'] === $item2['Key_name']) {
$k++;
}
}
if ($k === 1 && $item['Column_name'] === 'number') {
$indexes[] = $item['Key_name'];
}
}
$isFound = false;
$oldIndexes = [];
foreach ($indexes as $key) {
if ($key === 'UNIQ_NUMBER') {
$isFound = true;
}
}
if (!$isFound) {
try {
$sql = "CREATE UNIQUE INDEX UNIQ_NUMBER ON `note` (`number`)";
$pdo->query($sql);
} catch (\Exception $e) {}
}
foreach ($indexes as $item) {
if ($item === 'UNIQ_NUMBER') continue;
try {
$sql = "DROP INDEX {$item} ON `note`";
$pdo->query($sql);
} catch (\Exception $e) {}
}
}
}

View File

@@ -0,0 +1,90 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
public function run($container)
{
$this->container = $container;
$this->checkDatabaseRequirements();
}
protected function checkDatabaseRequirements()
{
$databaseRequirements = [
'mysql' => '5.7.0',
'mariadb' => '10.1.0',
];
$databaseHelper = new \Espo\Core\Utils\Database\Helper($this->container->get('config'));
$pdo = $this->container->get('entityManager')->getPDO();
$databaseType = $databaseHelper->getDatabaseType();
$fullVersion = $databaseHelper->getPdoDatabaseVersion($pdo);
if (preg_match('/[0-9]+\.[0-9]+\.[0-9]+/', $fullVersion, $match)) {
$version = $match[0];
$databaseTypeLc = strtolower($databaseType);
if (isset($databaseRequirements[$databaseTypeLc])) {
$requiredVersion = $databaseRequirements[$databaseTypeLc];
if (version_compare($version, $requiredVersion, '<')) {
$msg = "Your {$databaseType} version is not supported. Please upgrade {$databaseType} to a newer version ({$requiredVersion} or later).";
throw new \Espo\Core\Exceptions\Error($msg);
}
}
}
$query = "
ALTER TABLE `user` ADD `middle_name` VARCHAR(100) DEFAULT NULL COLLATE utf8mb4_unicode_ci
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
$query = "
UPDATE email_account SET port = NULL WHERE port = ''
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
$query = "
UPDATE inbound_email SET port = NULL WHERE port = ''
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
}
}

7
upgrades/5.9/data.json Normal file
View File

@@ -0,0 +1,7 @@
{
"manifest": {
"delete": [
"vendor/zendframework"
]
}
}

View File

@@ -0,0 +1,117 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$this->container = $container;
$config = $container->get('config');
$config->set('busyRangesEntityList', ['Meeting', 'Call']);
$config->set('emailAddressLookupEntityTypeList', ['User', 'Contact', 'Lead', 'Account']);
$config->save();
$this->fixCollation($container);
}
protected function fixCollation($container)
{
$ignotedEntityList = ['Job', 'LayoutRecord'];
$pdo = $container->get('entityManager')->getPDO();
$ormMeta = $container->get('ormMetadata')->getData(true);
$fieldListExceededIndexMaxLength = \Espo\Core\Utils\Database\Schema\Utils::getFieldListExceededIndexMaxLength($ormMeta, 767);
foreach ($ormMeta as $entityName => $entityParams) {
if (in_array($entityName, $ignotedEntityList)) continue;
if (!isset($fieldListExceededIndexMaxLength[$entityName])) continue;
$tableName = \Espo\Core\Utils\Util::toUnderScore($entityName);
//Get table columns params
$query = "SHOW FULL COLUMNS FROM `". $tableName ."` WHERE `Collation` <> 'utf8mb4_unicode_ci'";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {
$GLOBALS['log']->debug('Utf8mb4: Table does not exist - ' . $e->getMessage());
continue;
}
$columnParams = array();
$rowList = $sth->fetchAll(\PDO::FETCH_ASSOC);
foreach ($rowList as $row) {
$columnParams[ $row['Field'] ] = $row;
}
//END: get table columns params
foreach ($entityParams['fields'] as $fieldName => $fieldParams) {
$columnName = \Espo\Core\Utils\Util::toUnderScore($fieldName);
if (!in_array($fieldName, $fieldListExceededIndexMaxLength[$entityName])) continue;
if (!isset($columnParams[$columnName])) continue;
if (isset($fieldParams['notStorable']) && $fieldParams['notStorable']) {
continue;
}
if (!isset($columnParams[$columnName]) || empty($columnParams[$columnName]['Type'])) {
continue;
}
$query = null;
switch ($fieldParams['type']) {
case 'varchar':
case 'text':
case 'jsonObject':
case 'jsonArray':
$query = "ALTER TABLE `".$tableName."` CHANGE COLUMN `". $columnName ."` `". $columnName ."` ". $columnParams[$columnName]['Type'] ." CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;";
break;
}
if (!empty($query)) {
$GLOBALS['log']->debug('Utf8mb4: execute the query - [' . $query . '].');
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {
$GLOBALS['log']->warning('Utf8mb4: FAILED executing the query - [' . $query . '], details: '. $e->getMessage() .'.');
}
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
{
"fields": {
"entityType": {
"type": "varchar",
"index": true,
"maxLength": 100
},
"fieldName": {
"type": "varchar",
"maxLength": 100
},
"value": {
"type": "int",
"default": 1
}
}
}

4
upgrades/6.0/data.json Normal file
View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,76 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
public function run($container)
{
$this->container = $container;
$this->processEmailAccountsUpdate();
$this->cleanupFiles();
}
protected function processEmailAccountsUpdate()
{
$pdo = $this->container->get('entityManager')->getPDO();
$q1 = "UPDATE `email_account` SET email_account.security = '' WHERE email_account.ssl = 0";
$q2 = "UPDATE `inbound_email` SET inbound_email.security = '' WHERE inbound_email.ssl = 0";
$pdo->exec($q1);
$pdo->exec($q2);
}
protected function cleanupFiles()
{
$fileManager = $this->container->get('fileManager');
$fileList = [
'application/Espo/Core/Loaders/ClientManager.php',
'client/res/templates/record/panels/default-side.tpl',
'application/Espo/Resources/templates/noteEmailRecieved',
];
foreach ($fileList as $file) {
if (is_dir($file)) {
$fileManager->removeInDir($file, true);
continue;
}
if (!file_exists($file)) {
continue;
}
unlink($file);
}
}
}

View File

@@ -0,0 +1,185 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Exceptions\Error;
class BeforeUpgrade
{
public function run($container)
{
$this->container = $container;
$this->processCheckCLI();
$this->processCheckExtensions();
$this->processMyIsamCheck();
$this->processNextNumberAlterTable();
}
protected function processCheckCLI()
{
$isCli = (substr(php_sapi_name(), 0, 3) == 'cli') ? true : false;
if (!$isCli) {
throw new Error("This upgrade can be run only from CLI.");
}
}
protected function processCheckExtensions()
{
$em = $this->container->get('entityManager');
$extension = $em->getRepository('Extension')
->where([
'name' => 'Google Integration',
'isInstalled' => true,
])
->findOne();
if ($extension) {
$version = $extension->get('version');
if (version_compare($version, '1.4.2', '<')) {
$message =
"EspoCRM 6.0.0 is not compatible with Google Integration extension of a version lower than 1.4.2. " .
"Please upgrade the extension or uninstall it. Then run the upgrade command again.";
throw new Error($message);
}
}
$extension = $em->getRepository('Extension')
->where([
'name' => 'Real Estate',
'isInstalled' => true,
])
->findOne();
if ($extension) {
$version = $extension->get('version');
if (version_compare($version, '1.4.0', '<')) {
$message =
"EspoCRM 6.0.0 is not compatible with Real Estate extension of a version lower than 1.4.0. " .
"Please upgrade the extension or uninstall it. Then run the upgrade command again.";
throw new Error($message);
}
}
$extension = $em->getRepository('Extension')
->where([
'name' => 'VoIP Integration',
'isInstalled' => true,
])
->findOne();
if ($extension) {
$version = $extension->get('version');
if (version_compare($version, '1.15.0', '<')) {
$message =
"EspoCRM 6.0.0 is not compatible with VoIP Integration extension of a version lower than 1.15.0. " .
"Please upgrade the extension or uninstall it. Then run the upgrade command again.";
throw new Error($message);
}
}
}
protected function processMyIsamCheck()
{
$myisamTableList = $this->getMyIsamTableList();
if (empty($myisamTableList)) {
return;
}
$isCli = (substr(php_sapi_name(), 0, 3) == 'cli') ? true : false;
$tableListString = implode(", ", $myisamTableList);
$lineBreak = $isCli ? "\n" : "<br>";
$link = "https://www.espocrm.com/blog/converting-myisam-engine-to-innodb";
$linkString = $isCli ? $link : "<a href=\"{$link}\" target=\"_blank\">link</a>";
$message =
"In v6.0 we have dropped a support of MyISAM engine for DB tables. " .
"You have the following tables that use MyISAM: {$tableListString}.{$lineBreak}{$lineBreak}" .
"Please change the engine to InnoDB for these tables then run upgrade again.{$lineBreak}{$lineBreak}" .
"See: {$linkString}.";
throw new Error($message);
}
protected function getMyIsamTableList()
{
$container = $this->container;
$pdo = $container->get('entityManager')->getPDO();
$databaseInfo = $container->get('config')->get('database');
try {
$sth = $pdo->prepare("
SELECT TABLE_NAME as tableName
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = '". $databaseInfo['dbname'] ."'
AND ENGINE = 'MyISAM'
");
$sth->execute();
}
catch (Exception $e) {
return [];
}
$tableList = $sth->fetchAll(PDO::FETCH_COLUMN);
if (empty($tableList)) {
return [];
}
return $tableList;
}
protected function processNextNumberAlterTable()
{
$pdo = $this->container->get('entityManager')->getPDO();
$q1 = "ALTER TABLE `next_number` CHANGE `entity_type` `entity_type` VARCHAR(100)";
$q2 = "ALTER TABLE `next_number` CHANGE `field_name` `field_name` VARCHAR(100)";
$pdo->exec($q1);
$pdo->exec($q2);
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,84 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
private $container;
public function run($container)
{
$this->container = $container;
$this->removeUnnecessaryFiles();
$this->removeUnnecessaryDirectories();
}
public function removeUnnecessaryFiles()
{
$fileList = [
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.idx',
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.idx',
];
foreach ($fileList as $file) {
if (!file_exists($file)) {
continue;
}
$result = unlink($file);
if (!$result) {
$this->container->get('fileManager')->getPermissionUtils()->chmod($file, [
'file' => '0664',
'dir' => '0775',
]);
unlink($file);
}
}
}
public function removeUnnecessaryDirectories()
{
$directoryList = [
'vendor/spatie/async/.git',
'vendor/zordius/lightncandy/.git',
];
foreach ($directoryList as $directory) {
if (!file_exists($directory)) {
continue;
}
$this->container->get('fileManager')->removeInDir($directory, true);
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
private $container;
public function run($container)
{
$this->container = $container;
$this->removeUnnecessaryFiles();
$this->removeUnnecessaryDirectories();
}
public function removeUnnecessaryFiles()
{
$fileList = [
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.idx',
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.idx',
];
foreach ($fileList as $file) {
if (!file_exists($file)) {
continue;
}
$result = unlink($file);
if (!$result) {
$this->container->get('fileManager')->getPermissionUtils()->chmod($file, [
'file' => '0664',
'dir' => '0775',
]);
unlink($file);
}
}
}
public function removeUnnecessaryDirectories()
{
$directoryList = [
'vendor/spatie/async/.git',
'vendor/zordius/lightncandy/.git',
];
foreach ($directoryList as $directory) {
if (!file_exists($directory)) {
continue;
}
$this->container->get('fileManager')->removeInDir($directory, true);
}
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,52 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
private $container;
public function run($container)
{
$this->container = $container;
$this->fixTimeFormat();
}
protected function fixTimeFormat()
{
$config = $this->container->get('config');
$actualTimeFormat = $config->get('timeFormat');
if ($actualTimeFormat === 'hh:mm') {
$config->set('timeFormat', 'HH:mm');
$config->save();
}
}
}

4
upgrades/6.1/data.json Normal file
View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,101 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class AfterUpgrade
{
private $container;
public function run($container)
{
$this->container = $container;
$this->updateConfig();
$this->removeUnnecessaryFiles();
$this->removeUnnecessaryDirectories();
}
protected function updateConfig()
{
$config = $this->container->get('config');
$actualTimeFormat = $config->get('timeFormat');
if ($actualTimeFormat === 'hh:mm') {
$config->set('timeFormat', 'HH:mm');
}
$config->set('pdfEngine', 'Tcpdf');
$config->save();
}
protected function removeUnnecessaryFiles()
{
$fileList = [
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.idx',
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.idx',
];
foreach ($fileList as $file) {
if (!file_exists($file)) {
continue;
}
$result = unlink($file);
if (!$result) {
$this->container->get('fileManager')->getPermissionUtils()->chmod($file, [
'file' => '0664',
'dir' => '0775',
]);
unlink($file);
}
}
}
protected function removeUnnecessaryDirectories()
{
$directoryList = [
'vendor/spatie/async/.git',
'vendor/zordius/lightncandy/.git',
];
foreach ($directoryList as $directory) {
if (!file_exists($directory)) {
continue;
}
$this->container->get('fileManager')->removeInDir($directory, true);
}
}
}

View File

@@ -0,0 +1,84 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
private $container;
public function run($container)
{
$this->container = $container;
$this->removeUnnecessaryFiles();
$this->removeUnnecessaryDirectories();
}
public function removeUnnecessaryFiles()
{
$fileList = [
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.idx',
'vendor/spatie/async/.git/objects/pack/pack-14ab89d3ff365322e20cfd44252880928aaa4ed6.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.pack',
'vendor/zordius/lightncandy/.git/objects/pack/pack-8b009a4f84cb95d704fb194c5fee79c724dee033.idx',
];
foreach ($fileList as $file) {
if (!file_exists($file)) {
continue;
}
$result = unlink($file);
if (!$result) {
$this->container->get('fileManager')->getPermissionUtils()->chmod($file, [
'file' => '0664',
'dir' => '0775',
]);
unlink($file);
}
}
}
public function removeUnnecessaryDirectories()
{
$directoryList = [
'vendor/spatie/async/.git',
'vendor/zordius/lightncandy/.git',
];
foreach ($directoryList as $directory) {
if (!file_exists($directory)) {
continue;
}
$this->container->get('fileManager')->removeInDir($directory, true);
}
}
}

4
upgrades/7.0/data.json Normal file
View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,258 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Metadata;
use Espo\Core\Utils\File\Manager as FileManager;
use Espo\Core\Utils\Json;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Config\ConfigWriter;
use Espo\Core\Utils\File\Permission;
use Espo\ORM\EntityManager;
class AfterUpgrade
{
public function run(Container $container): void
{
$entityManager = $container->get('entityManager');
$this->updateTemplates($entityManager);
$this->updateEventMetadata($container->get('metadata'), $container->get('fileManager'));
$this->updatePersonMetadata($container->get('metadata'), $container->get('fileManager'));
$this->updateCompanyMetadata($container->get('metadata'), $container->get('fileManager'));
$this->migrateEmailAccountFolders('EmailAccount', $container->get('entityManager'));
$this->migrateEmailAccountFolders('InboundEmail', $container->get('entityManager'));
try {
$this->updateConfig(
$container->get('config'),
$container->get('injectableFactory')->create(ConfigWriter::class)
);
}
catch (\Throwable $e) {}
$this->setPermissions(
$container->get('injectableFactory')->create(Permission::class)
);
}
protected function updateTemplates($entityManager)
{
$templateList = $entityManager
->getRepository('Template')
->where([
'printHeader' => false,
['header!=' => null],
['header!=' => ''],
])
->find();
foreach ($templateList as $template) {
$body = $template->get('header') . ($template->get('body') ?? '');
$template->set('body', $body);
$template->set('header', null);
$entityManager->saveEntity($template, ['skipHooks' => true]);
}
}
private function updateEventMetadata(Metadata $metadata, FileManager $fileManager): void
{
$defs = $metadata->get(['scopes']);
$toSave = false;
$path1 = "application/Espo/Core/Templates/Metadata/Event/selectDefs.json";
$contents1 = $fileManager->getContents($path1);
$data1 = Json::decode($contents1, true);
$path2 = "application/Espo/Core/Templates/Metadata/Event/recordDefs.json";
$contents2 = $fileManager->getContents($path2);
$data2 = Json::decode($contents2, true);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== 'Event') {
continue;
}
$toSave = true;
$metadata->set('selectDefs', $entityType, $data1);
$metadata->set('recordDefs', $entityType, $data2);
}
if ($toSave) {
$metadata->save();
}
}
private function updatePersonMetadata(Metadata $metadata, FileManager $fileManager): void
{
$defs = $metadata->get(['scopes']);
$toSave = false;
$path2 = "application/Espo/Core/Templates/Metadata/Person/recordDefs.json";
$contents2 = $fileManager->getContents($path2);
$data2 = Json::decode($contents2, true);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== 'Person') {
continue;
}
$toSave = true;
$metadata->set('recordDefs', $entityType, $data2);
}
if ($toSave) {
$metadata->save();
}
}
private function updateCompanyMetadata(Metadata $metadata, FileManager $fileManager): void
{
$defs = $metadata->get(['scopes']);
$toSave = false;
$path2 = "application/Espo/Core/Templates/Metadata/Company/recordDefs.json";
$contents2 = $fileManager->getContents($path2);
$data2 = Json::decode($contents2, true);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== 'Company') {
continue;
}
$toSave = true;
$metadata->set('recordDefs', $entityType, $data2);
}
if ($toSave) {
$metadata->save();
}
}
private function migrateEmailAccountFolders(string $entityType, EntityManager $entityManager): void
{
$selectQuery = $entityManager->getQueryBuilder()
->select()
->from($entityType)
->select(['id', 'monitoredFolders'])
->build();
$sth = $entityManager->getQueryExecutor()->execute($selectQuery);
$dataList = [];
while ($row = $sth->fetch()) {
$dataList[] = [
'id' => $row['id'],
'folders' => $row['monitoredFolders'] ?? '',
];
}
foreach ($dataList as $item) {
$id = $item['id'];
$foldersString = $item['folders'];
$folders = array_map(
function (string $item): string {
return trim($item);
},
explode(',', $foldersString)
);
$foldersJsonString = json_encode($folders);
$updateQuery = $entityManager->getQueryBuilder()
->update()
->in($entityType)
->set(['monitoredFolders' => $foldersJsonString])
->where(['id' => $id])
->build();
$entityManager->getQueryExecutor()->execute($updateQuery);
}
}
private function updateConfig(Config $config, ConfigWriter $configWriter): void
{
$map = [];
$itemList = [
'cryptKey',
'hashSecretKey',
'apiSecretKeys',
'database',
'internalSmtpPassword',
'passwordSalt',
'webSocketSslCertificateLocalPrivateKey',
];
foreach ($itemList as $item) {
if (!$config->has($item)) {
continue;
}
$map[$item] = $config->get($item);
}
$configWriter->setMultiple($map);
$configWriter->save();
}
private function setPermissions(Permission $permission): void
{
$permission->chmod('bin/command', '0754');
}
}

View File

@@ -0,0 +1,116 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Exceptions\Error;
use Espo\Core\Container;
class BeforeUpgrade
{
public function run(Container $container)
{
$this->container = $container;
$this->processCheckExtensions();
$this->processCheckCache();
// Load to prevent fail if run in a single process.
$container->get('entityManager')
->getQueryBuilder()
->update()
->in('Test')
->set(['test' => 'test'])
->build();
}
private function processCheckCache()
{
$isCli = (substr(php_sapi_name(), 0, 3) == 'cli') ? true : false;
if (!$isCli) {
return;
}
$cacheParam = 'opcache.enable_cli';
$value = ini_get($cacheParam);
if ($value === '1') {
throw new Error("PHP parameter '{$cacheParam}' should be set to '0'.");
}
}
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '2.8.0', $errorMessageList);
$this->processCheckExtension('Sales Pack', '1.1.4', $errorMessageList);
$this->processCheckExtension('Outlook Integration', '1.2.5', $errorMessageList);
$this->processCheckExtension('MailChimp Integration', '1.0.8', $errorMessageList);
$this->processCheckExtension('Real Estate', '1.5.0', $errorMessageList);
$this->processCheckExtension('VoIP Integration', '1.17.3', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->get('entityManager');
$extension = $em->getRepository('Extension')
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 7.0 is not compatible with '{$name}' extension of a version lower than {$minVersion}. " .
"Please upgrade the extension or uninstall it. Then run the upgrade command again.";
$errorMessageList[] = $message;
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,41 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
public function run(): void
{
if (php_sapi_name() === 'cli' && !in_array('-s', $_SERVER['argv'])) {
echo "\n\nPlease re-run upgrade command with -s parameter:\n";
echo " php command.php upgrade -s\n\n";
throw new \Espo\Core\Exceptions\Error("Need -s parameter.");
}
}
}

View File

@@ -0,0 +1,4 @@
{
"manifest": {
}
}

View File

@@ -0,0 +1,41 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
class BeforeUpgrade
{
public function run(): void
{
if (php_sapi_name() === 'cli' && !in_array('-s', $_SERVER['argv'])) {
echo "\n\nPlease re-run upgrade command with -s parameter:\n";
echo " php command.php upgrade -s\n\n";
throw new \Espo\Core\Exceptions\Error("Need -s parameter.");
}
}
}

8
upgrades/7.1/data.json Normal file
View File

@@ -0,0 +1,8 @@
{
"manifest": {
"delete": [
"client/espo.min.js",
"client/espo.min.js.map"
]
}
}

View File

@@ -0,0 +1,52 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Config\ConfigWriter;
class AfterUpgrade
{
public function run(Container $container): void
{
try {
$this->updateConfig(
$container->get('config'),
$container->get('injectableFactory')->create(ConfigWriter::class)
);
}
catch (\Throwable $e) {}
}
private function updateConfig(Config $config, ConfigWriter $configWriter): void
{
$configWriter->set('recordsPerPageSelect', 10);
$configWriter->save();
}
}

View File

@@ -0,0 +1,5 @@
{
"mandatoryFiles": [
"bin/command"
]
}

View File

@@ -0,0 +1,14 @@
{
"mandatoryFiles": [
"application/Espo/Core/Authentication/LDAP/Client.php",
"application/Espo/Core/Authentication/LDAP/ClientFactory.php",
"application/Espo/Core/Authentication/LDAP/Utils.php"
],
"manifest": {
"delete": [
"application/Espo/Core/Authentication/Ldap/Client.php",
"application/Espo/Core/Authentication/Ldap/ClientFactory.php",
"application/Espo/Core/Authentication/Ldap/Utils.php"
]
}
}

19
upgrades/7.2/data.json Normal file
View File

@@ -0,0 +1,19 @@
{
"mandatoryFiles": [
"bin/command"
],
"manifest": {
"delete": [
"client/css/espo/espo-vertical.css",
"client/css/espo/espo-vertical-iframe.css",
"client/css/espo/dark-vertical.css",
"client/css/espo/dark-vertical-iframe.css",
"client/css/espo/hazyblue-vertical.css",
"client/css/espo/hazyblue-vertical-iframe.css",
"client/css/espo/sakura-vertical.css",
"client/css/espo/sakura-vertical-iframe.css",
"client/css/espo/violet-vertical.css",
"client/css/espo/violet-vertical-iframe.css"
]
}
}

View File

@@ -0,0 +1,178 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Metadata;
use Espo\Core\Utils\File\Manager as FileManager;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Config;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateEventMetadata($container->get('metadata'), $container->get('fileManager'));
$this->updateTheme($container->get('entityManager'), $container->get('config'));
$this->updateKbArticles($container->get('entityManager'));
}
private function updateEventMetadata(Metadata $metadata, FileManager $fileManager): void
{
$defs = $metadata->get(['scopes']);
$toSave = false;
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== 'Event') {
continue;
}
$toSave = true;
$metadata->set('recordDefs', $entityType, [
'beforeUpdateHookClassNameList' => [
"__APPEND__",
"Espo\\Classes\\RecordHooks\\Event\\BeforeUpdatePreserveDuration"
]
]);
$metadata->set('clientDefs', $entityType, [
'forcePatchAttributeDependencyMap' => [
"dateEnd" => ["dateStart"],
"dateEndDate" => ["dateStartDate"]
]
]);
if ($metadata->get(['entityDefs', $entityType, 'fields', 'isAllDay'])) {
$metadata->set('entityDefs', $entityType, [
'fields' => [
'isAllDay' => [
'readOnly' => false,
],
]
]);
}
}
if ($toSave) {
$metadata->save();
}
}
private function updateTheme(EntityManager $entityManager, Config $config): void
{
$themeList = [
'EspoVertical',
'HazyblueVertical',
'VioletVertical',
'SakuraVertical',
'DarkVertical',
];
$theme = $config->get('theme');
$navbar = 'top';
if (in_array($theme, $themeList)) {
$theme = substr($theme, 0, -8);
$navbar = 'side';
}
$config->set('theme', $theme);
$config->set('themeParams', (object) ['navbar' => $navbar]);
$config->save();
$userList = $entityManager->getRDBRepository(\Espo\Entities\User::ENTITY_TYPE)
->where([
'type' => ['regular', 'admin']
])
->find();
foreach ($userList as $user) {
$preferences = $entityManager->getEntityById(\Espo\Entities\Preferences::ENTITY_TYPE, $user->getId());
if (!$preferences) {
continue;
}
$theme = $preferences->get('theme');
$navbar = 'top';
if (!$theme) {
continue;
}
if (in_array($theme, $themeList)) {
$theme = substr($theme, 0, -8);
$navbar = 'side';
}
$preferences->set('theme', $theme);
$preferences->set('themeParams', (object) ['navbar' => $navbar]);
$entityManager->saveEntity($preferences);
}
$portalList = $entityManager->getRDBRepository(\Espo\Entities\Portal::ENTITY_TYPE)
->where([
'theme!=' => null,
])
->find();
foreach ($portalList as $portal) {
$theme = $portal->get('theme');
$navbar = 'top';
if (in_array($theme, $themeList)) {
$theme = substr($theme, 0, -8);
$navbar = 'side';
}
$portal->set('theme', $theme);
$portal->set('themeParams', (object) ['navbar' => $navbar]);
$entityManager->saveEntity($portal);
}
}
private function updateKbArticles(EntityManager $entityManager): void
{
$query = $entityManager
->getQueryBuilder()
->update()
->in(\Espo\Modules\Crm\Entities\KnowledgeBaseArticle::ENTITY_TYPE)
->where(['type' => null])
->set(['type' => 'Article'])
->build();
$entityManager->getQueryExecutor()->execute($query);
}
}

2
upgrades/7.3/data.json Normal file
View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,145 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\EmailAccount;
use Espo\Entities\User;
use Espo\Entities\Preferences;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Database\Helper as DatabaseHelper;
class BeforeUpgrade
{
public function run(Container $container): void
{
$this->addUserColumn($container->get('config'));
$this->migrateSmtp($container->get('entityManager'));
}
private function migrateSmtp(EntityManager $entityManager): void
{
$users = $entityManager
->getRDBRepositoryByClass(User::class)
->where([
'isActive' => true,
'type' => [
User::TYPE_REGULAR,
User::TYPE_ADMIN,
]
])
->find();
foreach ($users as $user) {
$this->migrateSmtpForUser($entityManager, $user);
}
}
private function migrateSmtpForUser(EntityManager $entityManager, User $user): void
{
$preferences = $entityManager->getEntityById(Preferences::ENTITY_TYPE, $user->getId());
if (!$preferences) {
return;
}
$emailAddress = $user->getEmailAddress();
$smtpServer = $preferences->get('smtpServer');
if (!$smtpServer) {
return;
}
if (!$emailAddress) {
return;
}
$existingAccount = $entityManager
->getRDBRepositoryByClass(EmailAccount::class)
->where([
'assignedUserId' => $user->getId(),
'emailAddress' => $emailAddress,
'useSmtp' => true,
'status' => 'Active',
])
->findOne();
if ($existingAccount) {
return;
}
$account = $entityManager
->getRDBRepositoryByClass(EmailAccount::class)
->getNew();
$account->set([
'assignedUserId' => $user->getId(),
'name' => $emailAddress . ' (auto-created)',
'emailAddress' => $emailAddress,
'useImap' => false,
'useSmtp' => true,
'status' => 'Active',
'smtpHost' => $smtpServer,
'smtpPort' => $preferences->get('smtpPort'),
'smtpAuth' => $preferences->get('smtpAuth'),
'smtpSecurity' => $preferences->get('smtpSecurity'),
'smtpUsername' => $preferences->get('smtpUsername'),
'smtpPassword' => $preferences->get('smtpPassword'),
]);
$entityManager->saveEntity($account);
$preferences->set('smtpServer', null);
$preferences->set('smtpPort', null);
$preferences->set('smtpAuth', null);
$preferences->set('smtpAuth', null);
$preferences->set('smtpSecurity', null);
$preferences->set('smtpUsername', null);
$preferences->set('smtpPassword', null);
$entityManager->saveEntity($preferences);
}
private function addUserColumn(Config $config)
{
$databaseHelper = new DatabaseHelper($config);
$pdo = $databaseHelper->getDatabaseType();
$query = "
ALTER TABLE `user` ADD `working_time_calendar_id` VARCHAR(24)
DEFAULT NULL COLLATE `utf8mb4_unicode_ci`
";
try {
$sth = $pdo->prepare($query);
$sth->execute();
} catch (\Exception $e) {}
}
}

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,68 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Metadata;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateMetadata($container->get('metadata'));
}
private function updateMetadata(Metadata $metadata): void
{
$this->fixParent($metadata);
$metadata->save();
}
private function fixParent(Metadata $metadata): void
{
foreach ($metadata->get(['entityDefs']) as $scope => $defs) {
foreach ($metadata->get(['entityDefs', $scope, 'fields']) as $field => $fieldDefs) {
$custom = $metadata->getCustom('entityDefs', $scope);
if (!$custom) {
continue;
}
if (
($fieldDefs['type'] ?? null) === 'linkParent' &&
($fieldDefs['notStorable'] ?? false)
) {
if ($custom?->fields?->$field?->notStorable) {
$metadata->delete('entityDefs', $scope, "fields.{$field}.notStorable");
}
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,80 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Metadata;
use Espo\Core\Templates\Entities\Event;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateMetadata($container->get('metadata'));
}
private function updateMetadata(Metadata $metadata): void
{
$this->updateEventMetadata($metadata);
$metadata->save();
}
private function updateEventMetadata(Metadata $metadata): void
{
$defs = $metadata->get(['scopes']);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== Event::TEMPLATE_TYPE) {
continue;
}
if (!is_string($metadata->get(['entityDefs', $entityType, 'fields', 'duration', 'select']))) {
continue;
}
$metadata->delete('entityDefs', $entityType, 'fields.duration.orderBy');
$metadata->set('entityDefs', $entityType, [
'fields' => [
'duration' => [
'select' => [
'select' => "TIMESTAMPDIFF_SECOND:(dateStart, dateEnd)"
],
'order' => [
'order' => [["TIMESTAMPDIFF_SECOND:(dateStart, dateEnd)", "{direction}"]]
],
]
]
]);
}
}
}

2
upgrades/7.4/data.json Normal file
View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,107 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Utils\Metadata;
use Espo\Core\Templates\Entities\Event;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateMetadata($container->get('metadata'));
}
private function updateMetadata(Metadata $metadata): void
{
$metadata->set('app', 'recordId', [
'length' => 24,
]);
$this->fixParent($metadata);
$this->updateEventMetadata($metadata);
$metadata->save();
}
private function fixParent(Metadata $metadata): void
{
foreach ($metadata->get(['entityDefs']) as $scope => $defs) {
foreach ($metadata->get(['entityDefs', $scope, 'fields']) as $field => $fieldDefs) {
$custom = $metadata->getCustom('entityDefs', $scope);
if (!$custom) {
continue;
}
if (
($fieldDefs['type'] ?? null) === 'linkParent' &&
($fieldDefs['notStorable'] ?? false)
) {
if ($custom?->fields?->$field?->notStorable) {
$metadata->delete('entityDefs', $scope, "fields.{$field}.notStorable");
}
}
}
}
}
private function updateEventMetadata(Metadata $metadata): void
{
$defs = $metadata->get(['scopes']);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== Event::TEMPLATE_TYPE) {
continue;
}
if (!is_string($metadata->get(['entityDefs', $entityType, 'fields', 'duration', 'select']))) {
continue;
}
$metadata->delete('entityDefs', $entityType, 'fields.duration.orderBy');
$metadata->set('entityDefs', $entityType, [
'fields' => [
'duration' => [
'select' => [
'select' => "TIMESTAMPDIFF_SECOND:(dateStart, dateEnd)"
],
'order' => [
'order' => [["TIMESTAMPDIFF_SECOND:(dateStart, dateEnd)", "{direction}"]]
],
]
]
]);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Exceptions\Error;
use Espo\Core\Container;
class BeforeUpgrade
{
private ?Container $container = null;
public function run(Container $container)
{
$this->container = $container;
$this->processCheckExtensions();
}
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '2.13.9', $errorMessageList);
$this->processCheckExtension('Real Estate', '1.6.2', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->get('entityManager');
$extension = $em->getRDBRepository('Extension')
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 7.4 is not compatible with '{$name}' extension of versions lower than {$minVersion}. " .
"Please upgrade the extension or uninstall it.";
$errorMessageList[] = $message;
}
}

2
upgrades/7.5/data.json Normal file
View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -0,0 +1,76 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Templates\Entities\Event;
use Espo\Core\Utils\File\Manager as FileManager;
use Espo\Core\Utils\Json;
use Espo\Core\Utils\Metadata;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateEventMetadata(
$container->getByClass(Metadata::class),
$container->getByClass(FileManager::class)
);
}
private function updateEventMetadata(Metadata $metadata, FileManager $fileManager): void
{
$defs = $metadata->get(['scopes']);
$path1 = "application/Espo/Core/Templates/Metadata/Event/selectDefs.json";
$contents1 = $fileManager->getContents($path1);
$data1 = Json::decode($contents1, true);
$primaryFilterClassNameMap = (object) $data1['primaryFilterClassNameMap'];
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom || $type !== Event::TEMPLATE_TYPE) {
continue;
}
$data1 = $metadata->getCustom('selectDefs', $entityType) ?? (object) [];
$data1->primaryFilterClassNameMap = $primaryFilterClassNameMap;
$metadata->saveCustom('selectDefs', $entityType, $data1);
$data2 = $metadata->getCustom('scopes', $entityType) ?? (object) [];
$data2->completedStatusList = ['Held'];
$data2->canceledStatusList = ['Not Held'];
$metadata->saveCustom('scopes', $entityType, $data2);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\User;
use Espo\ORM\EntityManager;
use Espo\ORM\Query\DeleteBuilder;
class BeforeUpgrade
{
public function run(Container $container)
{
$this->deleteDeletedUsers($container->getByClass(EntityManager::class));
}
private function deleteDeletedUsers(EntityManager $entityManager): void
{
$query = DeleteBuilder::create()
->from(User::ENTITY_TYPE)
->where(['deleted' => true])
->build();
$entityManager->getQueryExecutor()->execute($query);
}
}

View File

@@ -0,0 +1,10 @@
{
"manifest": {
"delete": [
"client/lib/original/espo.js",
"client/lib/espo.min.js",
"client/lib/espo.min.js.map",
"client/cfg/pre-load.json"
]
}
}

View File

@@ -0,0 +1,119 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\Templates\Entities\Event;
use Espo\Entities\Role;
use Espo\ORM\EntityManager;
use Espo\ORM\Query\Part\Expression;
use Espo\ORM\Query\UpdateBuilder;
use Espo\Core\Templates\Entities\Company;
use Espo\Core\Templates\Entities\Person;
use Espo\Core\Templates\Entities\Base;
use Espo\Core\Templates\Entities\BasePlus;
use Espo\Core\Utils\Metadata;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateRoles(
$container->getByClass(EntityManager::class)
);
$this->updateMetadata(
$container->getByClass(Metadata::class)
);
}
private function updateRoles(EntityManager $entityManager): void
{
$query = UpdateBuilder::create()
->in(Role::ENTITY_TYPE)
->set(['messagePermission' => Expression::column('assignmentPermission')])
->build();
$entityManager->getQueryExecutor()->execute($query);
}
private function updateMetadata(Metadata $metadata): void
{
$defs = $metadata->get(['scopes']);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom) {
continue;
}
if ($type === Event::TEMPLATE_TYPE) {
$metadata->set('entityDefs', $entityType, [
'fields' => [
'dateEnd' => [
'suppressValidationList' => ['required'],
],
]
]);
$metadata->save();
continue;
}
if (
!in_array($type, [
BasePlus::TEMPLATE_TYPE,
Base::TEMPLATE_TYPE,
Company::TEMPLATE_TYPE,
Person::TEMPLATE_TYPE
])
) {
continue;
}
$recordDefs = $metadata->getCustom('recordDefs', $entityType) ?? (object) [];
$scopes = $metadata->getCustom('scopes', $entityType) ?? (object) [];
$recordDefs->duplicateWhereBuilderClassName = "Espo\\Classes\\DuplicateWhereBuilders\\General";
if (!isset($scopes->duplicateCheckFieldList)) {
$scopes->duplicateCheckFieldList = [];
if ($type === Company::TEMPLATE_TYPE || $type === Person::TEMPLATE_TYPE) {
$scopes->duplicateCheckFieldList = ['name', 'emailAddress'];
}
}
$metadata->saveCustom('recordDefs', $entityType, $recordDefs);
$metadata->saveCustom('scopes', $entityType, $scopes);
}
}
}

10
upgrades/8.0/data.json Normal file
View File

@@ -0,0 +1,10 @@
{
"manifest": {
"delete": [
"client/lib/original/espo.js",
"client/lib/espo.min.js",
"client/lib/espo.min.js.map",
"client/cfg/pre-load.json"
]
}
}

View File

@@ -0,0 +1,112 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\Role;
use Espo\ORM\EntityManager;
use Espo\ORM\Query\Part\Expression;
use Espo\ORM\Query\UpdateBuilder;
use Espo\Core\Utils\Metadata;
class AfterUpgrade
{
public function run(Container $container): void
{
$this->updateRoles(
$container->getByClass(EntityManager::class)
);
$this->updateMetadata(
$container->getByClass(Metadata::class)
);
}
private function updateRoles(EntityManager $entityManager): void
{
$query = UpdateBuilder::create()
->in(Role::ENTITY_TYPE)
->set(['messagePermission' => Expression::column('assignmentPermission')])
->build();
$entityManager->getQueryExecutor()->execute($query);
}
private function updateMetadata(Metadata $metadata): void
{
$defs = $metadata->get(['scopes']);
foreach ($defs as $entityType => $item) {
$isCustom = $item['isCustom'] ?? false;
$type = $item['type'] ?? false;
if (!$isCustom) {
continue;
}
if ($type === 'Event') {
$metadata->set('entityDefs', $entityType, [
'fields' => [
'dateEnd' => [
'suppressValidationList' => ['required'],
],
]
]);
$metadata->save();
continue;
}
if (
!in_array($type, [
'BasePlus',
'Base',
'Company',
'Person',
])
) {
continue;
}
$recordDefs = $metadata->getCustom('recordDefs', $entityType) ?? (object) [];
$scopes = $metadata->getCustom('scopes', $entityType) ?? (object) [];
$recordDefs->duplicateWhereBuilderClassName = "Espo\\Classes\\DuplicateWhereBuilders\\General";
$scopes->duplicateCheckFieldList = [];
if ($type === 'Company' || $type === 'Person') {
$scopes->duplicateCheckFieldList = ['name', 'emailAddress'];
}
$metadata->saveCustom('recordDefs', $entityType, $recordDefs);
$metadata->saveCustom('scopes', $entityType, $scopes);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Exceptions\Error;
use Espo\Core\Container;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Error
*/
public function run(Container $container)
{
$this->container = $container;
$this->processCheckExtensions();
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '3.0.0', $errorMessageList);
$this->processCheckExtension('Google Integration', '1.6.0', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->get('entityManager');
$extension = $em->getRDBRepository('Extension')
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 8.0 is not compatible with '$name' extension of versions lower than $minVersion. " .
"Please upgrade the extension or uninstall it.";
$errorMessageList[] = $message;
}
}

10
upgrades/8.1/data.json Normal file
View File

@@ -0,0 +1,10 @@
{
"manifest": {
"delete": [
"client/lib/original/espo.js",
"client/lib/espo.min.js",
"client/lib/espo.min.js.map",
"client/cfg/pre-load.json"
]
}
}

View File

@@ -0,0 +1,54 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Config\ConfigWriter;
class AfterUpgrade
{
public function run(Container $container): void
{
$config = $container->getByClass(Espo\Core\Utils\Config::class);
$configWriter = $container->getByClass(InjectableFactory::class)
->create(ConfigWriter::class);
$configWriter->setMultiple([
'phoneNumberNumericSearch' => false,
'phoneNumberInternational' => false,
]);
if ($config->get('pdfEngine') === 'Tcpdf') {
$configWriter->set('pdfEngine', 'Dompdf');
}
$configWriter->save();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Exceptions\Error;
use Espo\Core\Container;
use Espo\Core\Utils\Config;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
/**
* @throws Error
*/
public function run(Container $container): void
{
$this->checkLogHandlers($container);
}
/**
* @throws Error
*/
private function checkLogHandlers(Container $container): void
{
$config = $container->getByClass(Config::class);
if (!$config->get('logger.handlerList')) {
return;
}
$msg = "You need to remove logger.handlerList from the `data/config-internal.php` before upgrading. " .
"In EspoCRM v8.1, Monolog library was updated, custom log handlers may be incompatible. ".
"You will be able to return the handlers back after the upgrade.";
throw new Error($msg);
}
}

5
upgrades/8.2/data.json Normal file
View File

@@ -0,0 +1,5 @@
{
"manifest": {
"delete": []
}
}

View File

@@ -0,0 +1,119 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Config;
use Espo\Core\Utils\Config\ConfigWriter;
use Espo\Core\Utils\Metadata;
use Espo\Entities\Template;
use Espo\ORM\EntityManager;
use Espo\Tools\Pdf\Template as PdfTemplate;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class AfterUpgrade
{
public function run(Container $container): void
{
$configWriter = $container->getByClass(InjectableFactory::class)
->create(ConfigWriter::class);
$configWriter->setMultiple([
'jobForceUtc' => true,
]);
$configWriter->save();
$em = $container->getByClass(EntityManager::class);
$config = $container->getByClass(Config::class);
$this->updateTemplates($em, $config);
$this->updateTargetList($container->getByClass(Metadata::class));
}
private function updateTemplates(EntityManager $entityManager, Config $config): void
{
if ($config->get('pdfEngine') !== 'Dompdf') {
return;
}
/** @var iterable<Template> $templates */
$templates = $entityManager->getRDBRepositoryByClass(Template::class)
->sth()
->where(['pageFormat' => PdfTemplate::PAGE_FORMAT_CUSTOM])
->find();
foreach ($templates as $template) {
$width = $template->get('pageWidth') ?? 0.0;
$height = $template->get('pageHeight') ?? 0.0;
$template->setMultiple([
'pageWidth' => $width / 2.83465,
'pageHeight' => $height / 2.83465,
]);
$entityManager->saveEntity($template);
}
}
private function updateTargetList(Metadata $metadata): void
{
$links = $metadata->get('entityDefs.TargetList.links') ?? [];
$toSave = false;
foreach ($links as $link => $defs) {
if (empty($defs['isCustom'])) {
continue;
}
if (!$metadata->get("clientDefs.TargetList.relationshipPanels.$link.massSelect")) {
continue;
}
$metadata->set('recordDefs', 'TargetList', [
'relationships' => [
$link => [
'massLink' => true,
'linkRequiredForeignAccess' => 'read',
'mandatoryAttributeList' => ['targetListIsOptedOut'],
]
]
]);
$toSave = true;
}
if (!$toSave) {
return;
}
$metadata->save();
}
}

View File

@@ -0,0 +1,148 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\ORM\EntityManager;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Exception
*/
public function run(Container $container): void
{
$this->container = $container;
$this->processCheckExtensions();
$this->checkRepositories($container->getByClass(EntityManager::class));
}
/**
* @throws Exception
*/
private function checkRepositories(EntityManager $em): void
{
/** @var class-string[] $classNameList */
$classNameList = [
"Espo\\Repositories\\ActionHistoryRecord",
"Espo\\Repositories\\NextNumber",
"Espo\\Repositories\\AuthLogRecord",
"Espo\\Repositories\\AuthToken",
"Espo\\Modules\\Crm\\Repositories\\Account",
"Espo\\Modules\\Crm\\Repositories\\Call",
"Espo\\Modules\\Crm\\Repositories\\CaseObj",
"Espo\\Modules\\Crm\\Repositories\\Contact",
"Espo\\Modules\\Crm\\Repositories\\KnowledgeBaseArticle",
"Espo\\Modules\\Crm\\Repositories\\Lead",
"Espo\\Modules\\Crm\\Repositories\\Meeting",
"Espo\\Modules\\Crm\\Repositories\\Opportunity",
"Espo\\Modules\\Crm\\Repositories\\TargetList",
"Espo\\Modules\\Crm\\Repositories\\Task",
];
$list = [];
foreach ($em->getMetadata()->getEntityTypeList() as $entityType) {
if (!$em->hasRepository($entityType)) {
continue;
}
$repository = $em->getRepository($entityType);
if (in_array(get_class($repository), $classNameList)) {
continue;
}
foreach ($classNameList as $className) {
if (is_a($repository, $className)) {
$list[] = get_class($repository);
}
}
}
if ($list === []) {
return;
}
$msg = implode(', ', $list) .
" should extend from Espo\\Core\\Repositories\\Database. Fix before upgrading.";
throw new Exception($msg);
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '3.1.0', $errorMessageList);
$this->processCheckExtension('Real Estate', '1.8.0', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->get('entityManager');
$extension = $em->getRDBRepository('Extension')
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 8.2 is not compatible with '$name' extension of versions lower than $minVersion. " .
"Please upgrade the extension or uninstall it.";
$errorMessageList[] = $message;
}
}

3
upgrades/9.0/data.json Normal file
View File

@@ -0,0 +1,3 @@
{
"manifest": {}
}

View File

@@ -0,0 +1,183 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\Extension;
use Espo\ORM\EntityManager;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Exception
*/
public function run(Container $container): void
{
$this->container = $container;
$this->processCheckExtensions();
$this->checkBadClasses([
'Espo\\Core\\Services\\Base',
'Espo\\Core\\ORM\\Repositories\\RDB',
'Espo\\Core\\Hooks\\Base',
'Espo\\Core\\Acl\\Base',
'Espo\\Core\\Acl\\Acl',
'Espo\\Core\\AclPortal\\Base',
'Espo\\Core\\AclPortal\\Acl',
'Espo\\Core\\Injectable',
]);
}
/**
* @param string[] $badList
* @throws Exception
* @noinspection PhpSameParameterValueInspection
*/
private function checkBadClasses(array $badList): void
{
$skipPathRegexList = [
'^custom\/Espo\/Modules\/[^\/]+\/vendor',
'^custom\/Espo\/Custom\/vendor',
'^application\/Espo\/Modules\/[^\/]+\/vendor',
"^custom\/Espo\/Modules\/Sales\/",
];
foreach (['custom', 'application/Espo/Modules'] as $dir) {
$this->loadFromDir($dir, $skipPathRegexList);
}
$ignoreList = [
'Espo\\Core\\Acl\\Acl',
'Espo\\Core\\AclPortal\\Acl',
'Espo\\Core\\Cleanup\\Base',
];
$msg = '';
foreach (get_declared_classes() as $class) {
if (in_array($class, $ignoreList)) {
continue;
}
foreach ($badList as $badClass) {
if (is_subclass_of($class, $badClass) && $class !== $badClass) {
$msg .= " $class is sub-class of $badClass\n";
}
}
}
if ($msg !== '') {
$msg = "These classes should be fixed before upgrade. " .
"They extend classes that are removed in the next version.\n\n" . $msg;
throw new Exception($msg);
}
}
private function loadFromDir(string $dir, array $skipPathRegexList): void
{
$directory = new RecursiveDirectoryIterator($dir);
$fullTree = new RecursiveIteratorIterator($directory);
$phpFiles = new RegexIterator($fullTree, '/^.+\.php$/i', RegexIterator::GET_MATCH);
foreach ($phpFiles as $path) {
$file = $path[0];
if ($this->toSkip($file, $skipPathRegexList)) {
continue;
}
require_once($file);
}
}
private function toSkip(string $file, array $skipPathRegexList): bool
{
foreach ($skipPathRegexList as $pattern) {
if (preg_match('/' . $pattern . '/', $file)) {
return true;
}
}
return false;
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Advanced Pack', '3.2.0', $errorMessageList);
$this->processCheckExtension('VoIP Integration', '2.0.0', $errorMessageList);
$this->processCheckExtension('Real Estate', '1.8.2', $errorMessageList);
$this->processCheckExtension('Google Integration', '1.7.6', $errorMessageList);
$this->processCheckExtension('Outlook Integration', '1.3.6', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->getByClass(EntityManager::class);
$extension = $em->getRDBRepository(Extension::ENTITY_TYPE)
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 9.0 is not compatible with '$name' extension of versions lower than $minVersion. " .
"Please upgrade the extension or uninstall it.";
$errorMessageList[] = $message;
}
}

3
upgrades/9.2/data.json Normal file
View File

@@ -0,0 +1,3 @@
{
"manifest": {}
}

View File

@@ -0,0 +1,96 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\Extension;
use Espo\ORM\EntityManager;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Exception
*/
public function run(Container $container): void
{
$this->container = $container;
$this->processCheckExtensions();
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Google Integration', '1.8.2', $errorMessageList);
$this->processCheckExtension('Outlook Integration', '1.6.7', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->getByClass(EntityManager::class);
$extension = $em->getRDBRepository(Extension::ENTITY_TYPE)
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 9.2 is not compatible with '$name' extension of versions lower than $minVersion. " .
"Please upgrade the extension or uninstall it.";
$errorMessageList[] = $message;
}
}

3
upgrades/9.3/data.json Normal file
View File

@@ -0,0 +1,3 @@
{
"manifest": {}
}

View File

@@ -0,0 +1,95 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
use Espo\Core\Container;
use Espo\Entities\Extension;
use Espo\ORM\EntityManager;
/** @noinspection PhpMultipleClassDeclarationsInspection */
class BeforeUpgrade
{
private ?Container $container = null;
/**
* @throws Exception
*/
public function run(Container $container): void
{
$this->container = $container;
$this->processCheckExtensions();
}
/**
* @throws Error
*/
private function processCheckExtensions(): void
{
$errorMessageList = [];
$this->processCheckExtension('Google Integration', '1.8.3', $errorMessageList);
$this->processCheckExtension('Outlook Integration', '1.6.12', $errorMessageList);
if (!count($errorMessageList)) {
return;
}
$message = implode("\n\n", $errorMessageList);
throw new Error($message);
}
private function processCheckExtension(string $name, string $minVersion, array &$errorMessageList): void
{
$em = $this->container->getByClass(EntityManager::class);
$extension = $em->getRDBRepository(Extension::ENTITY_TYPE)
->where([
'name' => $name,
'isInstalled' => true,
])
->findOne();
if (!$extension) {
return;
}
$version = $extension->get('version');
if (version_compare($version, $minVersion, '>=')) {
return;
}
$message =
"EspoCRM 9.3 is not compatible with '$name' extension of versions lower than $minVersion. " .
"You need to upgrade the extension.";
$errorMessageList[] = $message;
}
}